summaryrefslogtreecommitdiff
path: root/indra/llrender
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llrender')
-rw-r--r--indra/llrender/llcubemap.cpp674
-rw-r--r--indra/llrender/llcubemaparray.cpp370
-rw-r--r--indra/llrender/llcubemaparray.h152
-rw-r--r--indra/llrender/llfontbitmapcache.cpp372
-rw-r--r--indra/llrender/llfontbitmapcache.h162
-rw-r--r--indra/llrender/llfontfreetype.cpp1772
-rw-r--r--indra/llrender/llfontfreetype.h386
-rw-r--r--indra/llrender/llfontfreetypesvg.h108
-rw-r--r--indra/llrender/llfontgl.cpp2640
-rw-r--r--indra/llrender/llfontgl.h496
-rw-r--r--indra/llrender/llfontregistry.cpp1524
-rw-r--r--indra/llrender/llgl.cpp5888
-rw-r--r--indra/llrender/llgl.h952
-rw-r--r--indra/llrender/llglslshader.cpp3904
-rw-r--r--indra/llrender/llglslshader.h688
-rw-r--r--indra/llrender/llgltexture.cpp790
-rw-r--r--indra/llrender/llgltexture.h412
-rw-r--r--indra/llrender/llimagegl.cpp5094
-rw-r--r--indra/llrender/llimagegl.h724
-rw-r--r--indra/llrender/llpostprocess.cpp908
-rw-r--r--indra/llrender/llrender.cpp4532
-rw-r--r--indra/llrender/llrender2dutils.cpp3164
-rw-r--r--indra/llrender/llrender2dutils.h354
-rw-r--r--indra/llrender/llshadermgr.cpp2846
-rw-r--r--indra/llrender/llshadermgr.h694
-rw-r--r--indra/llrender/lltexturemanagerbridge.h94
-rw-r--r--indra/llrender/lluiimage.inl154
-rw-r--r--indra/llrender/llvertexbuffer.h552
28 files changed, 20203 insertions, 20203 deletions
diff --git a/indra/llrender/llcubemap.cpp b/indra/llrender/llcubemap.cpp
index e06802a8e5..26e4aaad52 100644
--- a/indra/llrender/llcubemap.cpp
+++ b/indra/llrender/llcubemap.cpp
@@ -1,337 +1,337 @@
-/**
- * @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 "llglslshader.h"
-
-#include "llglheaders.h"
-
-namespace {
- const U16 RESOLUTION = 64;
-}
-
-bool LLCubeMap::sUseCubeMaps = true;
-
-LLCubeMap::LLCubeMap(bool init_as_srgb)
- : mTextureStage(0),
- mMatrixStage(0),
- mIssRGB(init_as_srgb)
-{
- mTargets[0] = GL_TEXTURE_CUBE_MAP_NEGATIVE_X;
- mTargets[1] = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
- mTargets[2] = GL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
- mTargets[3] = GL_TEXTURE_CUBE_MAP_POSITIVE_Y;
- mTargets[4] = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
- mTargets[5] = GL_TEXTURE_CUBE_MAP_POSITIVE_Z;
-}
-
-LLCubeMap::~LLCubeMap()
-{
-}
-
-void LLCubeMap::initGL()
-{
- llassert(gGLManager.mInited);
-
- if (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(RESOLUTION, RESOLUTION, 4, false);
- #if USE_SRGB_DECODE
- if (mIssRGB) {
- mImages[i]->setExplicitFormat(GL_SRGB8_ALPHA8, GL_RGBA);
- }
- #endif
- mImages[i]->setTarget(mTargets[i], LLTexUnit::TT_CUBE_MAP);
- mRawImages[i] = new LLImageRaw(RESOLUTION, RESOLUTION, 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
- {
- LL_WARNS() << "Using cube map without extension!" << LL_ENDL;
- }
-}
-
-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++)
- {
- LLImageDataSharedLock lockIn(rawimages[i]);
- LLImageDataLock lockOut(mRawImages[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()
-{
- LL_PROFILE_ZONE_SCOPED;
- for (int i = 0; i < 6; i++)
- {
- mImages[i]->setSubImage(mRawImages[i], 0, 0, RESOLUTION, RESOLUTION);
- }
-}
-
-void LLCubeMap::init(const std::vector<LLPointer<LLImageRaw> >& rawimages)
-{
- if (!gGLManager.mIsDisabled)
- {
- initGL();
- initRawData(rawimages);
- initGLData();
- }
-}
-
-void LLCubeMap::initReflectionMap(U32 resolution, U32 components)
-{
- U32 texname = 0;
-
- LLImageGL::generateTextures(1, &texname);
-
- mImages[0] = new LLImageGL(resolution, resolution, components, true);
- mImages[0]->setTexName(texname);
- mImages[0]->setTarget(mTargets[0], LLTexUnit::TT_CUBE_MAP);
- gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname);
- mImages[0]->setAddressMode(LLTexUnit::TAM_CLAMP);
-}
-
-void LLCubeMap::initEnvironmentMap(const std::vector<LLPointer<LLImageRaw> >& rawimages)
-{
- llassert(rawimages.size() == 6);
-
- U32 texname = 0;
-
- LLImageGL::generateTextures(1, &texname);
-
- U32 resolution = rawimages[0]->getWidth();
- U32 components = rawimages[0]->getComponents();
-
- for (int i = 0; i < 6; i++)
- {
- llassert(rawimages[i]->getWidth() == resolution);
- llassert(rawimages[i]->getHeight() == resolution);
- llassert(rawimages[i]->getComponents() == components);
-
- mImages[i] = new LLImageGL(resolution, resolution, components, true);
- mImages[i]->setTarget(mTargets[i], LLTexUnit::TT_CUBE_MAP);
- mRawImages[i] = rawimages[i];
- mImages[i]->createGLTexture(0, mRawImages[i], texname);
-
- gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname);
- mImages[i]->setAddressMode(LLTexUnit::TAM_CLAMP);
- stop_glerror();
-
- mImages[i]->setSubImage(mRawImages[i], 0, 0, resolution, resolution);
- }
- enableTexture(0);
- bind();
- mImages[0]->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
- glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
- glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
- gGL.getTexUnit(0)->disable();
- disable();
-}
-
-void LLCubeMap::generateMipMaps()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
-
- mImages[0]->setUseMipMaps(true);
- mImages[0]->setHasMipMaps(true);
- enableTexture(0);
- bind();
- mImages[0]->setFilteringOption(LLTexUnit::TFO_BILINEAR);
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("cmgmm - glGenerateMipmap");
- glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
- }
- gGL.getTexUnit(0)->disable();
- disable();
-}
-
-GLuint LLCubeMap::getGLName()
-{
- return mImages[0]->getTexName();
-}
-
-void LLCubeMap::bind()
-{
- gGL.getTexUnit(mTextureStage)->bind(this);
-}
-
-void LLCubeMap::enable(S32 stage)
-{
- enableTexture(stage);
-}
-
-void LLCubeMap::enableTexture(S32 stage)
-{
- mTextureStage = stage;
- if (stage >= 0 && LLCubeMap::sUseCubeMaps)
- {
- gGL.getTexUnit(stage)->enable(LLTexUnit::TT_CUBE_MAP);
- }
-}
-
-void LLCubeMap::disable(void)
-{
- disableTexture();
-}
-
-void LLCubeMap::disableTexture(void)
-{
- if (mTextureStage >= 0 && LLCubeMap::sUseCubeMaps)
- {
- gGL.getTexUnit(mTextureStage)->disable();
- if (mTextureStage == 0)
- {
- gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
- }
- }
-}
-
-void LLCubeMap::setMatrix(S32 stage)
-{
- mMatrixStage = stage;
-
- if (mMatrixStage < 0) return;
-
- //if (stage > 0)
- {
- gGL.getTexUnit(stage)->activate();
- }
-
- LLVector3 x(gGLModelView+0);
- LLVector3 y(gGLModelView+4);
- LLVector3 z(gGLModelView+8);
-
- LLMatrix3 mat3;
- mat3.setRows(x,y,z);
- LLMatrix4 trans(mat3);
- trans.transpose();
-
- gGL.matrixMode(LLRender::MM_TEXTURE);
- gGL.pushMatrix();
- gGL.loadMatrix((F32 *)trans.mMatrix);
- gGL.matrixMode(LLRender::MM_MODELVIEW);
-
- /*if (stage > 0)
- {
- gGL.getTexUnit(0)->activate();
- }*/
-}
-
-void LLCubeMap::restoreMatrix()
-{
- if (mMatrixStage < 0) return;
-
- //if (mMatrixStage > 0)
- {
- gGL.getTexUnit(mMatrixStage)->activate();
- }
- gGL.matrixMode(LLRender::MM_TEXTURE);
- gGL.popMatrix();
- gGL.matrixMode(LLRender::MM_MODELVIEW);
-
- /*if (mMatrixStage > 0)
- {
- gGL.getTexUnit(0)->activate();
- }*/
-}
-
-
-void LLCubeMap::destroyGL()
-{
- for (S32 i = 0; i < 6; i++)
- {
- mImages[i] = NULL;
- }
-}
+/**
+ * @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 "llglslshader.h"
+
+#include "llglheaders.h"
+
+namespace {
+ const U16 RESOLUTION = 64;
+}
+
+bool LLCubeMap::sUseCubeMaps = true;
+
+LLCubeMap::LLCubeMap(bool init_as_srgb)
+ : mTextureStage(0),
+ mMatrixStage(0),
+ mIssRGB(init_as_srgb)
+{
+ mTargets[0] = GL_TEXTURE_CUBE_MAP_NEGATIVE_X;
+ mTargets[1] = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
+ mTargets[2] = GL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
+ mTargets[3] = GL_TEXTURE_CUBE_MAP_POSITIVE_Y;
+ mTargets[4] = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
+ mTargets[5] = GL_TEXTURE_CUBE_MAP_POSITIVE_Z;
+}
+
+LLCubeMap::~LLCubeMap()
+{
+}
+
+void LLCubeMap::initGL()
+{
+ llassert(gGLManager.mInited);
+
+ if (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(RESOLUTION, RESOLUTION, 4, false);
+ #if USE_SRGB_DECODE
+ if (mIssRGB) {
+ mImages[i]->setExplicitFormat(GL_SRGB8_ALPHA8, GL_RGBA);
+ }
+ #endif
+ mImages[i]->setTarget(mTargets[i], LLTexUnit::TT_CUBE_MAP);
+ mRawImages[i] = new LLImageRaw(RESOLUTION, RESOLUTION, 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
+ {
+ LL_WARNS() << "Using cube map without extension!" << LL_ENDL;
+ }
+}
+
+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++)
+ {
+ LLImageDataSharedLock lockIn(rawimages[i]);
+ LLImageDataLock lockOut(mRawImages[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()
+{
+ LL_PROFILE_ZONE_SCOPED;
+ for (int i = 0; i < 6; i++)
+ {
+ mImages[i]->setSubImage(mRawImages[i], 0, 0, RESOLUTION, RESOLUTION);
+ }
+}
+
+void LLCubeMap::init(const std::vector<LLPointer<LLImageRaw> >& rawimages)
+{
+ if (!gGLManager.mIsDisabled)
+ {
+ initGL();
+ initRawData(rawimages);
+ initGLData();
+ }
+}
+
+void LLCubeMap::initReflectionMap(U32 resolution, U32 components)
+{
+ U32 texname = 0;
+
+ LLImageGL::generateTextures(1, &texname);
+
+ mImages[0] = new LLImageGL(resolution, resolution, components, true);
+ mImages[0]->setTexName(texname);
+ mImages[0]->setTarget(mTargets[0], LLTexUnit::TT_CUBE_MAP);
+ gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname);
+ mImages[0]->setAddressMode(LLTexUnit::TAM_CLAMP);
+}
+
+void LLCubeMap::initEnvironmentMap(const std::vector<LLPointer<LLImageRaw> >& rawimages)
+{
+ llassert(rawimages.size() == 6);
+
+ U32 texname = 0;
+
+ LLImageGL::generateTextures(1, &texname);
+
+ U32 resolution = rawimages[0]->getWidth();
+ U32 components = rawimages[0]->getComponents();
+
+ for (int i = 0; i < 6; i++)
+ {
+ llassert(rawimages[i]->getWidth() == resolution);
+ llassert(rawimages[i]->getHeight() == resolution);
+ llassert(rawimages[i]->getComponents() == components);
+
+ mImages[i] = new LLImageGL(resolution, resolution, components, true);
+ mImages[i]->setTarget(mTargets[i], LLTexUnit::TT_CUBE_MAP);
+ mRawImages[i] = rawimages[i];
+ mImages[i]->createGLTexture(0, mRawImages[i], texname);
+
+ gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname);
+ mImages[i]->setAddressMode(LLTexUnit::TAM_CLAMP);
+ stop_glerror();
+
+ mImages[i]->setSubImage(mRawImages[i], 0, 0, resolution, resolution);
+ }
+ enableTexture(0);
+ bind();
+ mImages[0]->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
+ glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+ glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
+ gGL.getTexUnit(0)->disable();
+ disable();
+}
+
+void LLCubeMap::generateMipMaps()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+
+ mImages[0]->setUseMipMaps(true);
+ mImages[0]->setHasMipMaps(true);
+ enableTexture(0);
+ bind();
+ mImages[0]->setFilteringOption(LLTexUnit::TFO_BILINEAR);
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("cmgmm - glGenerateMipmap");
+ glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
+ }
+ gGL.getTexUnit(0)->disable();
+ disable();
+}
+
+GLuint LLCubeMap::getGLName()
+{
+ return mImages[0]->getTexName();
+}
+
+void LLCubeMap::bind()
+{
+ gGL.getTexUnit(mTextureStage)->bind(this);
+}
+
+void LLCubeMap::enable(S32 stage)
+{
+ enableTexture(stage);
+}
+
+void LLCubeMap::enableTexture(S32 stage)
+{
+ mTextureStage = stage;
+ if (stage >= 0 && LLCubeMap::sUseCubeMaps)
+ {
+ gGL.getTexUnit(stage)->enable(LLTexUnit::TT_CUBE_MAP);
+ }
+}
+
+void LLCubeMap::disable(void)
+{
+ disableTexture();
+}
+
+void LLCubeMap::disableTexture(void)
+{
+ if (mTextureStage >= 0 && LLCubeMap::sUseCubeMaps)
+ {
+ gGL.getTexUnit(mTextureStage)->disable();
+ if (mTextureStage == 0)
+ {
+ gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
+ }
+ }
+}
+
+void LLCubeMap::setMatrix(S32 stage)
+{
+ mMatrixStage = stage;
+
+ if (mMatrixStage < 0) return;
+
+ //if (stage > 0)
+ {
+ gGL.getTexUnit(stage)->activate();
+ }
+
+ LLVector3 x(gGLModelView+0);
+ LLVector3 y(gGLModelView+4);
+ LLVector3 z(gGLModelView+8);
+
+ LLMatrix3 mat3;
+ mat3.setRows(x,y,z);
+ LLMatrix4 trans(mat3);
+ trans.transpose();
+
+ gGL.matrixMode(LLRender::MM_TEXTURE);
+ gGL.pushMatrix();
+ gGL.loadMatrix((F32 *)trans.mMatrix);
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+
+ /*if (stage > 0)
+ {
+ gGL.getTexUnit(0)->activate();
+ }*/
+}
+
+void LLCubeMap::restoreMatrix()
+{
+ if (mMatrixStage < 0) return;
+
+ //if (mMatrixStage > 0)
+ {
+ gGL.getTexUnit(mMatrixStage)->activate();
+ }
+ gGL.matrixMode(LLRender::MM_TEXTURE);
+ gGL.popMatrix();
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+
+ /*if (mMatrixStage > 0)
+ {
+ gGL.getTexUnit(0)->activate();
+ }*/
+}
+
+
+void LLCubeMap::destroyGL()
+{
+ for (S32 i = 0; i < 6; i++)
+ {
+ mImages[i] = NULL;
+ }
+}
diff --git a/indra/llrender/llcubemaparray.cpp b/indra/llrender/llcubemaparray.cpp
index cba65a6211..be69b997da 100644
--- a/indra/llrender/llcubemaparray.cpp
+++ b/indra/llrender/llcubemaparray.cpp
@@ -1,185 +1,185 @@
-/**
- * @file llcubemaparray.cpp
- * @brief LLCubeMap class implementation
- *
- * $LicenseInfo:firstyear=2022&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2022, 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 "llcubemaparray.h"
-
-#include "v4coloru.h"
-#include "v3math.h"
-#include "v3dmath.h"
-#include "m3math.h"
-#include "m4math.h"
-
-#include "llrender.h"
-#include "llglslshader.h"
-
-#include "llglheaders.h"
-
-//#pragma optimize("", off)
-
-using namespace LLImageGLMemory;
-
-// MUST match order of OpenGL face-layers
-GLenum LLCubeMapArray::sTargets[6] =
-{
- GL_TEXTURE_CUBE_MAP_POSITIVE_X,
- GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
- GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
- GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
- GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
- GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
-};
-
-LLVector3 LLCubeMapArray::sLookVecs[6] =
-{
- LLVector3(1, 0, 0),
- LLVector3(-1, 0, 0),
- LLVector3(0, 1, 0),
- LLVector3(0, -1, 0),
- LLVector3(0, 0, 1),
- LLVector3(0, 0, -1)
-};
-
-LLVector3 LLCubeMapArray::sUpVecs[6] =
-{
- LLVector3(0, -1, 0),
- LLVector3(0, -1, 0),
- LLVector3(0, 0, 1),
- LLVector3(0, 0, -1),
- LLVector3(0, -1, 0),
- LLVector3(0, -1, 0)
-};
-
-LLVector3 LLCubeMapArray::sClipToCubeLookVecs[6] =
-{
- LLVector3(0, 0, -1), //GOOD
- LLVector3(0, 0, 1), //GOOD
-
- LLVector3(1, 0, 0), // GOOD
- LLVector3(1, 0, 0), // GOOD
-
- LLVector3(1, 0, 0),
- LLVector3(-1, 0, 0),
-};
-
-LLVector3 LLCubeMapArray::sClipToCubeUpVecs[6] =
-{
- LLVector3(-1, 0, 0), //GOOD
- LLVector3(1, 0, 0), //GOOD
-
- LLVector3(0, 1, 0), // GOOD
- LLVector3(0, -1, 0), // GOOD
-
- LLVector3(0, 0, -1),
- LLVector3(0, 0, 1)
-};
-
-LLCubeMapArray::LLCubeMapArray()
- : mTextureStage(0)
-{
-
-}
-
-LLCubeMapArray::~LLCubeMapArray()
-{
-}
-
-void LLCubeMapArray::allocate(U32 resolution, U32 components, U32 count, bool use_mips)
-{
- U32 texname = 0;
- mWidth = resolution;
- mCount = count;
-
- LLImageGL::generateTextures(1, &texname);
-
- mImage = new LLImageGL(resolution, resolution, components, use_mips);
- mImage->setTexName(texname);
- mImage->setTarget(sTargets[0], LLTexUnit::TT_CUBE_MAP_ARRAY);
-
- mImage->setUseMipMaps(use_mips);
- mImage->setHasMipMaps(use_mips);
-
- bind(0);
-
- U32 format = components == 4 ? GL_RGBA16F : GL_RGB16F;
-
- U32 mip = 0;
-
- free_cur_tex_image();
-
- while (resolution >= 1)
- {
- glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, format, resolution, resolution, count * 6, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
-
- if (!use_mips)
- {
- break;
- }
- resolution /= 2;
- ++mip;
- }
-
- alloc_tex_image(resolution * 6, resolution, format);
-
- mImage->setAddressMode(LLTexUnit::TAM_CLAMP);
-
- if (use_mips)
- {
- mImage->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
- //glGenerateMipmap(GL_TEXTURE_CUBE_MAP_ARRAY); // <=== latest AMD drivers do not appreciate this method of allocating mipmaps
- }
- else
- {
- mImage->setFilteringOption(LLTexUnit::TFO_BILINEAR);
- }
-
- unbind();
-}
-
-void LLCubeMapArray::bind(S32 stage)
-{
- mTextureStage = stage;
- gGL.getTexUnit(stage)->bindManual(LLTexUnit::TT_CUBE_MAP_ARRAY, getGLName(), mImage->getUseMipMaps());
-}
-
-void LLCubeMapArray::unbind()
-{
- gGL.getTexUnit(mTextureStage)->unbind(LLTexUnit::TT_CUBE_MAP_ARRAY);
- mTextureStage = -1;
-}
-
-GLuint LLCubeMapArray::getGLName()
-{
- return mImage->getTexName();
-}
-
-void LLCubeMapArray::destroyGL()
-{
- mImage = NULL;
-}
+/**
+ * @file llcubemaparray.cpp
+ * @brief LLCubeMap class implementation
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, 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 "llcubemaparray.h"
+
+#include "v4coloru.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "m3math.h"
+#include "m4math.h"
+
+#include "llrender.h"
+#include "llglslshader.h"
+
+#include "llglheaders.h"
+
+//#pragma optimize("", off)
+
+using namespace LLImageGLMemory;
+
+// MUST match order of OpenGL face-layers
+GLenum LLCubeMapArray::sTargets[6] =
+{
+ GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
+};
+
+LLVector3 LLCubeMapArray::sLookVecs[6] =
+{
+ LLVector3(1, 0, 0),
+ LLVector3(-1, 0, 0),
+ LLVector3(0, 1, 0),
+ LLVector3(0, -1, 0),
+ LLVector3(0, 0, 1),
+ LLVector3(0, 0, -1)
+};
+
+LLVector3 LLCubeMapArray::sUpVecs[6] =
+{
+ LLVector3(0, -1, 0),
+ LLVector3(0, -1, 0),
+ LLVector3(0, 0, 1),
+ LLVector3(0, 0, -1),
+ LLVector3(0, -1, 0),
+ LLVector3(0, -1, 0)
+};
+
+LLVector3 LLCubeMapArray::sClipToCubeLookVecs[6] =
+{
+ LLVector3(0, 0, -1), //GOOD
+ LLVector3(0, 0, 1), //GOOD
+
+ LLVector3(1, 0, 0), // GOOD
+ LLVector3(1, 0, 0), // GOOD
+
+ LLVector3(1, 0, 0),
+ LLVector3(-1, 0, 0),
+};
+
+LLVector3 LLCubeMapArray::sClipToCubeUpVecs[6] =
+{
+ LLVector3(-1, 0, 0), //GOOD
+ LLVector3(1, 0, 0), //GOOD
+
+ LLVector3(0, 1, 0), // GOOD
+ LLVector3(0, -1, 0), // GOOD
+
+ LLVector3(0, 0, -1),
+ LLVector3(0, 0, 1)
+};
+
+LLCubeMapArray::LLCubeMapArray()
+ : mTextureStage(0)
+{
+
+}
+
+LLCubeMapArray::~LLCubeMapArray()
+{
+}
+
+void LLCubeMapArray::allocate(U32 resolution, U32 components, U32 count, bool use_mips)
+{
+ U32 texname = 0;
+ mWidth = resolution;
+ mCount = count;
+
+ LLImageGL::generateTextures(1, &texname);
+
+ mImage = new LLImageGL(resolution, resolution, components, use_mips);
+ mImage->setTexName(texname);
+ mImage->setTarget(sTargets[0], LLTexUnit::TT_CUBE_MAP_ARRAY);
+
+ mImage->setUseMipMaps(use_mips);
+ mImage->setHasMipMaps(use_mips);
+
+ bind(0);
+
+ U32 format = components == 4 ? GL_RGBA16F : GL_RGB16F;
+
+ U32 mip = 0;
+
+ free_cur_tex_image();
+
+ while (resolution >= 1)
+ {
+ glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, format, resolution, resolution, count * 6, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+
+ if (!use_mips)
+ {
+ break;
+ }
+ resolution /= 2;
+ ++mip;
+ }
+
+ alloc_tex_image(resolution * 6, resolution, format);
+
+ mImage->setAddressMode(LLTexUnit::TAM_CLAMP);
+
+ if (use_mips)
+ {
+ mImage->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
+ //glGenerateMipmap(GL_TEXTURE_CUBE_MAP_ARRAY); // <=== latest AMD drivers do not appreciate this method of allocating mipmaps
+ }
+ else
+ {
+ mImage->setFilteringOption(LLTexUnit::TFO_BILINEAR);
+ }
+
+ unbind();
+}
+
+void LLCubeMapArray::bind(S32 stage)
+{
+ mTextureStage = stage;
+ gGL.getTexUnit(stage)->bindManual(LLTexUnit::TT_CUBE_MAP_ARRAY, getGLName(), mImage->getUseMipMaps());
+}
+
+void LLCubeMapArray::unbind()
+{
+ gGL.getTexUnit(mTextureStage)->unbind(LLTexUnit::TT_CUBE_MAP_ARRAY);
+ mTextureStage = -1;
+}
+
+GLuint LLCubeMapArray::getGLName()
+{
+ return mImage->getTexName();
+}
+
+void LLCubeMapArray::destroyGL()
+{
+ mImage = NULL;
+}
diff --git a/indra/llrender/llcubemaparray.h b/indra/llrender/llcubemaparray.h
index 9cfd3d1f38..675aaaf07c 100644
--- a/indra/llrender/llcubemaparray.h
+++ b/indra/llrender/llcubemaparray.h
@@ -1,76 +1,76 @@
-/**
- * @file llcubemaparray.h
- * @brief LLCubeMap class definition
- *
- * $LicenseInfo:firstyear=2022&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2022, 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$
- */
-
-#pragma once
-
-#include "llgl.h"
-
-#include <vector>
-
-class LLVector3;
-
-class LLCubeMapArray : public LLRefCount
-{
-public:
- LLCubeMapArray();
-
- static GLenum sTargets[6];
-
- // look and up vectors for each cube face (agent space)
- static LLVector3 sLookVecs[6];
- static LLVector3 sUpVecs[6];
-
- // look and up vectors for each cube face (clip space)
- static LLVector3 sClipToCubeLookVecs[6];
- static LLVector3 sClipToCubeUpVecs[6];
-
- // allocate a cube map array
- // res - resolution of each cube face
- // components - number of components per pixel
- // count - number of cube maps in the array
- // use_mips - if true, mipmaps will be allocated for this cube map array and anisotropic filtering will be used
- void allocate(U32 res, U32 components, U32 count, bool use_mips = true);
- void bind(S32 stage);
- void unbind();
-
- GLuint getGLName();
-
- void destroyGL();
-
- // get width of cubemaps in array (they're cubes, so this is also the height)
- U32 getWidth() const { return mWidth; }
-
- // get number of cubemaps in the array
- U32 getCount() const { return mCount; }
-
-protected:
- friend class LLTexUnit;
- ~LLCubeMapArray();
- LLPointer<LLImageGL> mImage;
- U32 mWidth = 0;
- U32 mCount = 0;
- S32 mTextureStage;
-};
+/**
+ * @file llcubemaparray.h
+ * @brief LLCubeMap class definition
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, 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$
+ */
+
+#pragma once
+
+#include "llgl.h"
+
+#include <vector>
+
+class LLVector3;
+
+class LLCubeMapArray : public LLRefCount
+{
+public:
+ LLCubeMapArray();
+
+ static GLenum sTargets[6];
+
+ // look and up vectors for each cube face (agent space)
+ static LLVector3 sLookVecs[6];
+ static LLVector3 sUpVecs[6];
+
+ // look and up vectors for each cube face (clip space)
+ static LLVector3 sClipToCubeLookVecs[6];
+ static LLVector3 sClipToCubeUpVecs[6];
+
+ // allocate a cube map array
+ // res - resolution of each cube face
+ // components - number of components per pixel
+ // count - number of cube maps in the array
+ // use_mips - if true, mipmaps will be allocated for this cube map array and anisotropic filtering will be used
+ void allocate(U32 res, U32 components, U32 count, bool use_mips = true);
+ void bind(S32 stage);
+ void unbind();
+
+ GLuint getGLName();
+
+ void destroyGL();
+
+ // get width of cubemaps in array (they're cubes, so this is also the height)
+ U32 getWidth() const { return mWidth; }
+
+ // get number of cubemaps in the array
+ U32 getCount() const { return mCount; }
+
+protected:
+ friend class LLTexUnit;
+ ~LLCubeMapArray();
+ LLPointer<LLImageGL> mImage;
+ U32 mWidth = 0;
+ U32 mCount = 0;
+ S32 mTextureStage;
+};
diff --git a/indra/llrender/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp
index 19ff9dc047..d2c40ad590 100644
--- a/indra/llrender/llfontbitmapcache.cpp
+++ b/indra/llrender/llfontbitmapcache.cpp
@@ -1,186 +1,186 @@
-/**
- * @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()
-
-{
-}
-
-LLFontBitmapCache::~LLFontBitmapCache()
-{
-}
-
-void LLFontBitmapCache::init(S32 max_char_width,
- S32 max_char_height)
-{
- reset();
-
- mMaxCharWidth = max_char_width;
- mMaxCharHeight = max_char_height;
-
- S32 image_width = mMaxCharWidth * 20;
- S32 pow_iw = 2;
- while (pow_iw < image_width)
- {
- pow_iw <<= 1;
- }
- image_width = pow_iw;
- image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever.
-
- mBitmapWidth = image_width;
- mBitmapHeight = image_width;
-}
-
-LLImageRaw *LLFontBitmapCache::getImageRaw(EFontGlyphType bitmap_type, U32 bitmap_num) const
-{
- const U32 bitmap_idx = static_cast<U32>(bitmap_type);
- if (bitmap_type >= EFontGlyphType::Count || bitmap_num >= mImageRawVec[bitmap_idx].size())
- return nullptr;
-
- return mImageRawVec[bitmap_idx][bitmap_num];
-}
-
-LLImageGL *LLFontBitmapCache::getImageGL(EFontGlyphType bitmap_type, U32 bitmap_num) const
-{
- const U32 bitmap_idx = static_cast<U32>(bitmap_type);
- if (bitmap_type >= EFontGlyphType::Count || bitmap_num >= mImageGLVec[bitmap_idx].size())
- return nullptr;
-
- return mImageGLVec[bitmap_idx][bitmap_num];
-}
-
-
-bool LLFontBitmapCache::nextOpenPos(S32 width, S32& pos_x, S32& pos_y, EFontGlyphType bitmap_type, U32& bitmap_num)
-{
- if (bitmap_type >= EFontGlyphType::Count)
- {
- return false;
- }
-
- const U32 bitmap_idx = static_cast<U32>(bitmap_type);
- if (mImageRawVec[bitmap_idx].empty() || (mCurrentOffsetX[bitmap_idx] + width + 1) > mBitmapWidth)
- {
- if ((mImageRawVec[bitmap_idx].empty()) || (mCurrentOffsetY[bitmap_idx] + 2*mMaxCharHeight + 2) > mBitmapHeight)
- {
- // We're out of space in the current image, or no image
- // has been allocated yet. Make a new one.
-
- 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;
-
- mBitmapWidth = image_width;
- mBitmapHeight = image_height;
-
- S32 num_components = getNumComponents(bitmap_type);
- mImageRawVec[bitmap_idx].push_back(new LLImageRaw(mBitmapWidth, mBitmapHeight, num_components));
- bitmap_num = mImageRawVec[bitmap_idx].size() - 1;
-
- LLImageRaw* image_raw = getImageRaw(bitmap_type, bitmap_num);
- if (EFontGlyphType::Grayscale == bitmap_type)
- {
- image_raw->clear(255, 0);
- }
-
- // Make corresponding GL image.
- mImageGLVec[bitmap_idx].push_back(new LLImageGL(image_raw, false));
- LLImageGL* image_gl = getImageGL(bitmap_type, bitmap_num);
-
- // Start at beginning of the new image.
- mCurrentOffsetX[bitmap_idx] = 1;
- mCurrentOffsetY[bitmap_idx] = 1;
-
- // Attach corresponding GL texture. (*TODO: is this needed?)
- 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[bitmap_idx] = 1;
- mCurrentOffsetY[bitmap_idx] += mMaxCharHeight + 1;
- }
- }
-
- pos_x = mCurrentOffsetX[bitmap_idx];
- pos_y = mCurrentOffsetY[bitmap_idx];
- bitmap_num = getNumBitmaps(bitmap_type) - 1;
-
- mCurrentOffsetX[bitmap_idx] += width + 1;
-
- return true;
-}
-
-void LLFontBitmapCache::destroyGL()
-{
- for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++)
- {
- for (LLImageGL* image_gl : mImageGLVec[idx])
- {
- image_gl->destroyGLTexture();
- }
- }
-}
-
-void LLFontBitmapCache::reset()
-{
- for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++)
- {
- mImageRawVec[idx].clear();
- mImageGLVec[idx].clear();
- mCurrentOffsetX[idx] = 1;
- mCurrentOffsetY[idx] = 1;
- }
-
- mBitmapWidth = 0;
- mBitmapHeight = 0;
-}
-
-//static
-U32 LLFontBitmapCache::getNumComponents(EFontGlyphType bitmap_type)
-{
- switch (bitmap_type)
- {
- case EFontGlyphType::Grayscale:
- return 2;
- case EFontGlyphType::Color:
- return 4;
- default:
- llassert(false);
- return 2;
- }
-}
+/**
+ * @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()
+
+{
+}
+
+LLFontBitmapCache::~LLFontBitmapCache()
+{
+}
+
+void LLFontBitmapCache::init(S32 max_char_width,
+ S32 max_char_height)
+{
+ reset();
+
+ mMaxCharWidth = max_char_width;
+ mMaxCharHeight = max_char_height;
+
+ S32 image_width = mMaxCharWidth * 20;
+ S32 pow_iw = 2;
+ while (pow_iw < image_width)
+ {
+ pow_iw <<= 1;
+ }
+ image_width = pow_iw;
+ image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever.
+
+ mBitmapWidth = image_width;
+ mBitmapHeight = image_width;
+}
+
+LLImageRaw *LLFontBitmapCache::getImageRaw(EFontGlyphType bitmap_type, U32 bitmap_num) const
+{
+ const U32 bitmap_idx = static_cast<U32>(bitmap_type);
+ if (bitmap_type >= EFontGlyphType::Count || bitmap_num >= mImageRawVec[bitmap_idx].size())
+ return nullptr;
+
+ return mImageRawVec[bitmap_idx][bitmap_num];
+}
+
+LLImageGL *LLFontBitmapCache::getImageGL(EFontGlyphType bitmap_type, U32 bitmap_num) const
+{
+ const U32 bitmap_idx = static_cast<U32>(bitmap_type);
+ if (bitmap_type >= EFontGlyphType::Count || bitmap_num >= mImageGLVec[bitmap_idx].size())
+ return nullptr;
+
+ return mImageGLVec[bitmap_idx][bitmap_num];
+}
+
+
+bool LLFontBitmapCache::nextOpenPos(S32 width, S32& pos_x, S32& pos_y, EFontGlyphType bitmap_type, U32& bitmap_num)
+{
+ if (bitmap_type >= EFontGlyphType::Count)
+ {
+ return false;
+ }
+
+ const U32 bitmap_idx = static_cast<U32>(bitmap_type);
+ if (mImageRawVec[bitmap_idx].empty() || (mCurrentOffsetX[bitmap_idx] + width + 1) > mBitmapWidth)
+ {
+ if ((mImageRawVec[bitmap_idx].empty()) || (mCurrentOffsetY[bitmap_idx] + 2*mMaxCharHeight + 2) > mBitmapHeight)
+ {
+ // We're out of space in the current image, or no image
+ // has been allocated yet. Make a new one.
+
+ 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;
+
+ mBitmapWidth = image_width;
+ mBitmapHeight = image_height;
+
+ S32 num_components = getNumComponents(bitmap_type);
+ mImageRawVec[bitmap_idx].push_back(new LLImageRaw(mBitmapWidth, mBitmapHeight, num_components));
+ bitmap_num = mImageRawVec[bitmap_idx].size() - 1;
+
+ LLImageRaw* image_raw = getImageRaw(bitmap_type, bitmap_num);
+ if (EFontGlyphType::Grayscale == bitmap_type)
+ {
+ image_raw->clear(255, 0);
+ }
+
+ // Make corresponding GL image.
+ mImageGLVec[bitmap_idx].push_back(new LLImageGL(image_raw, false));
+ LLImageGL* image_gl = getImageGL(bitmap_type, bitmap_num);
+
+ // Start at beginning of the new image.
+ mCurrentOffsetX[bitmap_idx] = 1;
+ mCurrentOffsetY[bitmap_idx] = 1;
+
+ // Attach corresponding GL texture. (*TODO: is this needed?)
+ 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[bitmap_idx] = 1;
+ mCurrentOffsetY[bitmap_idx] += mMaxCharHeight + 1;
+ }
+ }
+
+ pos_x = mCurrentOffsetX[bitmap_idx];
+ pos_y = mCurrentOffsetY[bitmap_idx];
+ bitmap_num = getNumBitmaps(bitmap_type) - 1;
+
+ mCurrentOffsetX[bitmap_idx] += width + 1;
+
+ return true;
+}
+
+void LLFontBitmapCache::destroyGL()
+{
+ for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++)
+ {
+ for (LLImageGL* image_gl : mImageGLVec[idx])
+ {
+ image_gl->destroyGLTexture();
+ }
+ }
+}
+
+void LLFontBitmapCache::reset()
+{
+ for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++)
+ {
+ mImageRawVec[idx].clear();
+ mImageGLVec[idx].clear();
+ mCurrentOffsetX[idx] = 1;
+ mCurrentOffsetY[idx] = 1;
+ }
+
+ mBitmapWidth = 0;
+ mBitmapHeight = 0;
+}
+
+//static
+U32 LLFontBitmapCache::getNumComponents(EFontGlyphType bitmap_type)
+{
+ switch (bitmap_type)
+ {
+ case EFontGlyphType::Grayscale:
+ return 2;
+ case EFontGlyphType::Color:
+ return 4;
+ default:
+ llassert(false);
+ return 2;
+ }
+}
diff --git a/indra/llrender/llfontbitmapcache.h b/indra/llrender/llfontbitmapcache.h
index 314a00a3a3..6b19ba2154 100644
--- a/indra/llrender/llfontbitmapcache.h
+++ b/indra/llrender/llfontbitmapcache.h
@@ -1,81 +1,81 @@
-/**
- * @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>
-#include "lltrace.h"
-
-enum class EFontGlyphType : U32
-{
- Grayscale = 0,
- Color,
- Count,
- Unspecified,
-};
-
-// Maintain a collection of bitmaps containing rendered glyphs.
-// Generalizes the single-bitmap logic from LLFontFreetype and LLFontGL.
-class LLFontBitmapCache
-{
-public:
- LLFontBitmapCache();
- ~LLFontBitmapCache();
-
- // Need to call this once, before caching any glyphs.
- void init(S32 max_char_width,
- S32 max_char_height);
-
- void reset();
-
- bool nextOpenPos(S32 width, S32& posX, S32& posY, EFontGlyphType bitmapType, U32& bitmapNum);
-
- void destroyGL();
-
- LLImageRaw* getImageRaw(EFontGlyphType bitmapType, U32 bitmapNum) const;
- LLImageGL* getImageGL(EFontGlyphType bitmapType, U32 bitmapNum) const;
-
- S32 getMaxCharWidth() const { return mMaxCharWidth; }
- U32 getNumBitmaps(EFontGlyphType bitmapType) const { return (bitmapType < EFontGlyphType::Count) ? mImageRawVec[static_cast<U32>(bitmapType)].size() : 0; }
- S32 getBitmapWidth() const { return mBitmapWidth; }
- S32 getBitmapHeight() const { return mBitmapHeight; }
-
-protected:
- static U32 getNumComponents(EFontGlyphType bitmap_type);
-
-private:
- S32 mBitmapWidth = 0;
- S32 mBitmapHeight = 0;
- S32 mCurrentOffsetX[static_cast<U32>(EFontGlyphType::Count)] = { 1 };
- S32 mCurrentOffsetY[static_cast<U32>(EFontGlyphType::Count)] = { 1 };
- S32 mMaxCharWidth = 0;
- S32 mMaxCharHeight = 0;
- std::vector<LLPointer<LLImageRaw>> mImageRawVec[static_cast<U32>(EFontGlyphType::Count)];
- std::vector<LLPointer<LLImageGL>> mImageGLVec[static_cast<U32>(EFontGlyphType::Count)];
-};
-
-#endif //LL_LLFONTBITMAPCACHE_H
+/**
+ * @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>
+#include "lltrace.h"
+
+enum class EFontGlyphType : U32
+{
+ Grayscale = 0,
+ Color,
+ Count,
+ Unspecified,
+};
+
+// Maintain a collection of bitmaps containing rendered glyphs.
+// Generalizes the single-bitmap logic from LLFontFreetype and LLFontGL.
+class LLFontBitmapCache
+{
+public:
+ LLFontBitmapCache();
+ ~LLFontBitmapCache();
+
+ // Need to call this once, before caching any glyphs.
+ void init(S32 max_char_width,
+ S32 max_char_height);
+
+ void reset();
+
+ bool nextOpenPos(S32 width, S32& posX, S32& posY, EFontGlyphType bitmapType, U32& bitmapNum);
+
+ void destroyGL();
+
+ LLImageRaw* getImageRaw(EFontGlyphType bitmapType, U32 bitmapNum) const;
+ LLImageGL* getImageGL(EFontGlyphType bitmapType, U32 bitmapNum) const;
+
+ S32 getMaxCharWidth() const { return mMaxCharWidth; }
+ U32 getNumBitmaps(EFontGlyphType bitmapType) const { return (bitmapType < EFontGlyphType::Count) ? mImageRawVec[static_cast<U32>(bitmapType)].size() : 0; }
+ S32 getBitmapWidth() const { return mBitmapWidth; }
+ S32 getBitmapHeight() const { return mBitmapHeight; }
+
+protected:
+ static U32 getNumComponents(EFontGlyphType bitmap_type);
+
+private:
+ S32 mBitmapWidth = 0;
+ S32 mBitmapHeight = 0;
+ S32 mCurrentOffsetX[static_cast<U32>(EFontGlyphType::Count)] = { 1 };
+ S32 mCurrentOffsetY[static_cast<U32>(EFontGlyphType::Count)] = { 1 };
+ S32 mMaxCharWidth = 0;
+ S32 mMaxCharHeight = 0;
+ std::vector<LLPointer<LLImageRaw>> mImageRawVec[static_cast<U32>(EFontGlyphType::Count)];
+ std::vector<LLPointer<LLImageGL>> mImageGLVec[static_cast<U32>(EFontGlyphType::Count)];
+};
+
+#endif //LL_LLFONTBITMAPCACHE_H
diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp
index 82404a2879..94b576885f 100644
--- a/indra/llrender/llfontfreetype.cpp
+++ b/indra/llrender/llfontfreetype.cpp
@@ -1,886 +1,886 @@
-/**
- * @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>
-#ifdef LL_WINDOWS
-#include <freetype2\freetype\ftsystem.h>
-#endif
-#include "llfontfreetypesvg.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 "lldir.h"
-#include "llerror.h"
-#include "llimage.h"
-#include "llimagepng.h"
-//#include "llimagej2c.h"
-#include "llmath.h" // Linden math
-#include "llstring.h"
-//#include "imdebug.h"
-#include "llfontbitmapcache.h"
-#include "llgl.h"
-
-#define ENABLE_OT_SVG_SUPPORT
-
-FT_Render_Mode gFontRenderMode = FT_RENDER_MODE_NORMAL;
-
-LLFontManager *gFontManagerp = NULL;
-
-FT_Library gFTLibrary = NULL;
-
-//static
-void LLFontManager::initClass()
-{
- if (!gFontManagerp)
- {
- 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.
- LL_ERRS() << "Freetype initialization failure!" << LL_ENDL;
- FT_Done_FreeType(gFTLibrary);
- }
-
-#ifdef ENABLE_OT_SVG_SUPPORT
- SVG_RendererHooks hooks = {
- LLFontFreeTypeSvgRenderer::OnInit,
- LLFontFreeTypeSvgRenderer::OnFree,
- LLFontFreeTypeSvgRenderer::OnRender,
- LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot,
- };
- FT_Property_Set(gFTLibrary, "ot-svg", "svg-hooks", &hooks);
-#endif
-}
-
-LLFontManager::~LLFontManager()
-{
- FT_Done_FreeType(gFTLibrary);
-}
-
-
-LLFontGlyphInfo::LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type)
-: mGlyphIndex(index),
- mGlyphType(glyph_type),
- 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
- mBitmapEntry(std::make_pair(EFontGlyphType::Unspecified, -1)) // Which bitmap in the bitmap cache contains this glyph
-{
-}
-
-LLFontGlyphInfo::LLFontGlyphInfo(const LLFontGlyphInfo& fgi)
- : mGlyphIndex(fgi.mGlyphIndex)
- , mGlyphType(fgi.mGlyphType)
- , mWidth(fgi.mWidth)
- , mHeight(fgi.mHeight)
- , mXAdvance(fgi.mXAdvance)
- , mYAdvance(fgi.mYAdvance)
- , mXBitmapOffset(fgi.mXBitmapOffset)
- , mYBitmapOffset(fgi.mYBitmapOffset)
- , mXBearing(fgi.mXBearing)
- , mYBearing(fgi.mYBearing)
-{
- mBitmapEntry = fgi.mBitmapEntry;
-}
-
-LLFontFreetype::LLFontFreetype()
-: mFontBitmapCachep(new LLFontBitmapCache),
- mAscender(0.f),
- mDescender(0.f),
- mLineHeight(0.f),
-#ifdef LL_WINDOWS
- pFileStream(NULL),
- pFtStream(NULL),
-#endif
- 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());
- mCharGlyphInfoMap.clear();
-
-#ifdef LL_WINDOWS
- delete pFileStream; // closed by FT_Done_Face
- delete pFtStream;
-#endif
- delete mFontBitmapCachep;
- // mFallbackFonts cleaned up by LLPointer destructor
-}
-
-#ifdef LL_WINDOWS
-unsigned long ft_read_cb(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count) {
- if (count <= 0) return count;
- llifstream *file_stream = static_cast<llifstream *>(stream->descriptor.pointer);
- file_stream->seekg(offset, std::ios::beg);
- file_stream->read((char*)buffer, count);
- return file_stream->gcount();
-}
-
-void ft_close_cb(FT_Stream stream) {
- llifstream *file_stream = static_cast<llifstream *>(stream->descriptor.pointer);
- file_stream->close();
-}
-#endif
-
-bool LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, bool is_fallback, S32 face_n)
-{
- // 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;
-#ifdef LL_WINDOWS
- error = ftOpenFace(filename, face_n);
-#else
- error = FT_New_Face( gFTLibrary,
- filename.c_str(),
- 0,
- &mFTFace);
-#endif
-
- if (error)
- {
-#ifdef LL_WINDOWS
- clearFontStreams();
-#endif
- 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);
-#ifdef LL_WINDOWS
- clearFontStreams();
-#endif
- 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 = ll_round(0.5f + (x_max - x_min));
- S32 max_char_height = ll_round(0.5f + (y_max - y_min));
-
- mFontBitmapCachep->init(max_char_width, max_char_height);
-
- if (!mFTFace->charmap)
- {
- //LL_INFOS() << " no unicode encoding, set whatever encoding there is..." << LL_ENDL;
- FT_Set_Charmap(mFTFace, mFTFace->charmaps[0]);
- }
-
- if (!mIsFallback)
- {
- // Add the default glyph
- addGlyphFromFont(this, 0, 0, EFontGlyphType::Grayscale);
- }
-
- mName = filename;
- mPointSize = point_size;
-
- mStyle = LLFontGL::NORMAL;
- if(mFTFace->style_flags & FT_STYLE_FLAG_BOLD)
- {
- mStyle |= LLFontGL::BOLD;
- }
-
- if(mFTFace->style_flags & FT_STYLE_FLAG_ITALIC)
- {
- mStyle |= LLFontGL::ITALIC;
- }
-
- return true;
-}
-
-S32 LLFontFreetype::getNumFaces(const std::string& filename)
-{
- if (mFTFace)
- {
- FT_Done_Face(mFTFace);
- mFTFace = NULL;
- }
-
- S32 num_faces = 1;
-
-#ifdef LL_WINDOWS
- int error = ftOpenFace(filename, 0);
-
- if (error)
- {
- return 0;
- }
- else
- {
- num_faces = mFTFace->num_faces;
- }
-
- FT_Done_Face(mFTFace);
- clearFontStreams();
- mFTFace = NULL;
-#endif
-
- return num_faces;
-}
-
-#ifdef LL_WINDOWS
-S32 LLFontFreetype::ftOpenFace(const std::string& filename, S32 face_n)
-{
- S32 error = -1;
- pFileStream = new llifstream(filename, std::ios::binary);
- if (pFileStream->is_open())
- {
- std::streampos beg = pFileStream->tellg();
- pFileStream->seekg(0, std::ios::end);
- std::streampos end = pFileStream->tellg();
- std::size_t file_size = end - beg;
- pFileStream->seekg(0, std::ios::beg);
-
- pFtStream = new LLFT_Stream();
- pFtStream->base = 0;
- pFtStream->pos = 0;
- pFtStream->size = file_size;
- pFtStream->descriptor.pointer = pFileStream;
- pFtStream->read = ft_read_cb;
- pFtStream->close = ft_close_cb;
-
- FT_Open_Args args;
- args.flags = FT_OPEN_STREAM;
- args.stream = (FT_StreamRec*)pFtStream;
- error = FT_Open_Face(gFTLibrary, &args, face_n, &mFTFace);
- }
- return error;
-}
-
-void LLFontFreetype::clearFontStreams()
-{
- if (pFileStream)
- {
- pFileStream->close();
- }
- delete pFileStream;
- delete pFtStream;
- pFileStream = NULL;
- pFtStream = NULL;
-}
-#endif
-
-void LLFontFreetype::addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font,
- const char_functor_t& functor)
-{
- mFallbackFonts.emplace_back(fallback_font, functor);
-}
-
-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, EFontGlyphType::Unspecified);
- 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, EFontGlyphType::Unspecified);;
- U32 left_glyph = left_glyph_info ? left_glyph_info->mGlyphIndex : 0;
- // Kern this puppy.
- LLFontGlyphInfo* right_glyph_info = getGlyphInfo(char_right, EFontGlyphType::Unspecified);
- 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, EFontGlyphType glyph_type) const
-{
- if (!mFTFace)
- {
- return NULL;
- }
-
- llassert(!mIsFallback);
- llassert(glyph_type < EFontGlyphType::Count);
- //LL_DEBUGS() << "Adding new glyph for " << wch << " to font" << LL_ENDL;
-
- // Initialize char to glyph map
- FT_UInt glyph_index = FT_Get_Char_Index(mFTFace, wch);
- if (glyph_index == 0)
- {
- // No corresponding glyph in this font: look for a glyph in fallback
- // fonts.
- size_t count = mFallbackFonts.size();
- if (LLStringOps::isEmoji(wch))
- {
- // This is a "genuine" emoji (in the range 0x1f000-0x20000): print
- // it using the emoji font(s) if possible. HB
- for (size_t i = 0; i < count; ++i)
- {
- const fallback_font_t& pair = mFallbackFonts[i];
- if (!pair.second || !pair.second(wch))
- {
- // If this font does not have a functor, or the character
- // does not pass the functor, reject it. Note: we keep the
- // functor test (despite the fact we already tested for
- // LLStringOps::isEmoji(wch) above), in case we would use
- // different, more restrictive or partionned functors in
- // the future with several different emoji fonts. HB
- continue;
- }
- glyph_index = FT_Get_Char_Index(pair.first->mFTFace, wch);
- if (glyph_index)
- {
- return addGlyphFromFont(pair.first, wch, glyph_index,
- glyph_type);
- }
- }
- }
- // Then try and find a monochrome fallback font that could print this
- // glyph: such fonts do *not* have a functor. We give priority to
- // monochrome fonts for non-genuine emojis so that UI elements which
- // used to render with them before the emojis font introduction (e.g.
- // check marks in menus, or LSL dialogs text and buttons) do render the
- // same way as they always did. HB
- std::vector<size_t> emoji_fonts_idx;
- for (size_t i = 0; i < count; ++i)
- {
- const fallback_font_t& pair = mFallbackFonts[i];
- if (pair.second)
- {
- // If this font got a functor, remember the index for later and
- // try the next fallback font. HB
- emoji_fonts_idx.push_back(i);
- continue;
- }
- glyph_index = FT_Get_Char_Index(pair.first->mFTFace, wch);
- if (glyph_index)
- {
- return addGlyphFromFont(pair.first, wch, glyph_index,
- glyph_type);
- }
- }
- // Everything failed so far: this character is not a genuine emoji,
- // neither a special character known from our monochrome fallback
- // fonts: make a last try, using the emoji font(s), but ignoring the
- // functor to render using whatever (colorful) glyph that might be
- // available in such fonts for this character. HB
- for (size_t j = 0, count2 = emoji_fonts_idx.size(); j < count2; ++j)
- {
- const fallback_font_t& pair = mFallbackFonts[emoji_fonts_idx[j]];
- glyph_index = FT_Get_Char_Index(pair.first->mFTFace, wch);
- if (glyph_index)
- {
- return addGlyphFromFont(pair.first, wch, glyph_index,
- glyph_type);
- }
- }
- }
-
- auto range_it = mCharGlyphInfoMap.equal_range(wch);
- char_glyph_info_map_t::iterator iter =
- std::find_if(range_it.first, range_it.second,
- [&glyph_type](const char_glyph_info_map_t::value_type& entry)
- {
- return entry.second->mGlyphType == glyph_type;
- });
- if (iter == range_it.second)
- {
- return addGlyphFromFont(this, wch, glyph_index, glyph_type);
- }
- return NULL;
-}
-
-LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index, EFontGlyphType requested_glyph_type) const
-{
- LL_PROFILE_ZONE_SCOPED;
- if (mFTFace == NULL)
- return NULL;
-
- llassert(!mIsFallback);
- fontp->renderGlyph(requested_glyph_type, glyph_index);
-
- EFontGlyphType bitmap_glyph_type = EFontGlyphType::Unspecified;
- switch (fontp->mFTFace->glyph->bitmap.pixel_mode)
- {
- case FT_PIXEL_MODE_MONO:
- case FT_PIXEL_MODE_GRAY:
- bitmap_glyph_type = EFontGlyphType::Grayscale;
- break;
- case FT_PIXEL_MODE_BGRA:
- bitmap_glyph_type = EFontGlyphType::Color;
- break;
- default:
- llassert_always(true);
- break;
- }
- S32 width = fontp->mFTFace->glyph->bitmap.width;
- S32 height = fontp->mFTFace->glyph->bitmap.rows;
-
- S32 pos_x, pos_y;
- U32 bitmap_num;
- mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_glyph_type, bitmap_num);
- mAddGlyphCount++;
-
- LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index, requested_glyph_type);
- gi->mXBitmapOffset = pos_x;
- gi->mYBitmapOffset = pos_y;
- gi->mBitmapEntry = std::make_pair(bitmap_glyph_type, 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);
-
- if (requested_glyph_type != bitmap_glyph_type)
- {
- LLFontGlyphInfo* gi_temp = new LLFontGlyphInfo(*gi);
- gi_temp->mGlyphType = bitmap_glyph_type;
- insertGlyphInfo(wch, gi_temp);
- }
-
- 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;
- }
-
- setSubImageLuminanceAlpha(pos_x,
- pos_y,
- bitmap_num,
- width,
- height,
- buffer_data,
- buffer_row_stride);
-
- if (tmp_graydata)
- delete[] tmp_graydata;
- }
- else if (fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)
- {
- setSubImageBGRA(pos_x,
- pos_y,
- bitmap_num,
- fontp->mFTFace->glyph->bitmap.width,
- fontp->mFTFace->glyph->bitmap.rows,
- fontp->mFTFace->glyph->bitmap.buffer,
- llabs(fontp->mFTFace->glyph->bitmap.pitch));
- } else {
- llassert(false);
- }
-
- LLImageGL *image_gl = mFontBitmapCachep->getImageGL(bitmap_glyph_type, bitmap_num);
- LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_glyph_type, bitmap_num);
- image_gl->setSubImage(image_raw, 0, 0, image_gl->getWidth(), image_gl->getHeight());
-
- return gi;
-}
-
-LLFontGlyphInfo* LLFontFreetype::getGlyphInfo(llwchar wch, EFontGlyphType glyph_type) const
-{
- std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch);
-
- char_glyph_info_map_t::iterator iter = (EFontGlyphType::Unspecified != glyph_type)
- ? std::find_if(range_it.first, range_it.second, [&glyph_type](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == glyph_type; })
- : range_it.first;
- if (iter != range_it.second)
- {
- return iter->second;
- }
- else
- {
- // this glyph doesn't yet exist, so render it and return the result
- return addGlyph(wch, (EFontGlyphType::Unspecified != glyph_type) ? glyph_type : EFontGlyphType::Grayscale);
- }
-}
-
-void LLFontFreetype::insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const
-{
- llassert(gi->mGlyphType < EFontGlyphType::Count);
- std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch);
-
- char_glyph_info_map_t::iterator iter =
- std::find_if(range_it.first, range_it.second, [&gi](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == gi->mGlyphType; });
- if (iter != range_it.second)
- {
- delete iter->second;
- iter->second = gi;
- }
- else
- {
- mCharGlyphInfoMap.insert(std::make_pair(wch, gi));
- }
-}
-
-void LLFontFreetype::renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index) const
-{
- if (mFTFace == NULL)
- return;
-
- FT_Int32 load_flags = FT_LOAD_FORCE_AUTOHINT;
- if (EFontGlyphType::Color == bitmap_type)
- {
- // We may not actually get a color render so our caller should always examine mFTFace->glyph->bitmap.pixel_mode
- load_flags |= FT_LOAD_COLOR;
- }
-
- FT_Error error = FT_Load_Glyph(mFTFace, glyph_index, load_flags);
- if (FT_Err_Ok != error)
- {
- std::string message = llformat(
- "Error %d (%s) loading glyph %u: bitmap_type=%u, load_flags=%d",
- error, FT_Error_String(error), glyph_index, bitmap_type, load_flags);
- LL_WARNS_ONCE() << message << LL_ENDL;
- error = FT_Load_Glyph(mFTFace, glyph_index, load_flags ^ FT_LOAD_COLOR);
- llassert_always_msg(FT_Err_Ok == error, message.c_str());
- }
-
- llassert_always(! FT_Render_Glyph(mFTFace->glyph, gFontRenderMode) );
-
- mRenderGlyphCount++;
-}
-
-void LLFontFreetype::reset(F32 vert_dpi, F32 horz_dpi)
-{
- resetBitmapCache();
- loadFace(mName, mPointSize, vert_dpi ,horz_dpi, mIsFallback, 0);
- if (!mIsFallback)
- {
- // This is the head of the list - need to rebuild ourself and all fallbacks.
- if (mFallbackFonts.empty())
- {
- LL_WARNS() << "LLFontGL::reset(), no fallback fonts present" << LL_ENDL;
- }
- else
- {
- for (fallback_font_vector_t::iterator it = mFallbackFonts.begin(); it != mFallbackFonts.end(); ++it)
- {
- it->first->reset(vert_dpi, horz_dpi);
- }
- }
- }
-}
-
-void LLFontFreetype::resetBitmapCache()
-{
- for (char_glyph_info_map_t::iterator it = mCharGlyphInfoMap.begin(), end_it = mCharGlyphInfoMap.end();
- it != end_it;
- ++it)
- {
- delete it->second;
- }
- 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, EFontGlyphType::Grayscale);
- }
-}
-
-void LLFontFreetype::destroyGL()
-{
- mFontBitmapCachep->destroyGL();
-}
-
-const std::string &LLFontFreetype::getName() const
-{
- return mName;
-}
-
-static void dumpFontBitmap(const LLImageRaw* image_raw, const std::string& file_name)
-{
- LLPointer<LLImagePNG> tmpImage = new LLImagePNG();
- if ( (tmpImage->encode(image_raw, 0.0f)) && (tmpImage->save(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, file_name))) )
- {
- LL_INFOS("Font") << "Successfully saved " << file_name << LL_ENDL;
- }
- else
- {
- LL_WARNS("Font") << "Failed to save " << file_name << LL_ENDL;
- }
-}
-
-void LLFontFreetype::dumpFontBitmaps() const
-{
- // Dump all the regular bitmaps (if any)
- for (int idx = 0, cnt = mFontBitmapCachep->getNumBitmaps(EFontGlyphType::Grayscale); idx < cnt; idx++)
- {
- dumpFontBitmap(mFontBitmapCachep->getImageRaw(EFontGlyphType::Grayscale, idx), llformat("%s_%d_%d_%d.png", mFTFace->family_name, (int)(mPointSize * 10), mStyle, idx));
- }
-
- // Dump all the color bitmaps (if any)
- for (int idx = 0, cnt = mFontBitmapCachep->getNumBitmaps(EFontGlyphType::Color); idx < cnt; idx++)
- {
- dumpFontBitmap(mFontBitmapCachep->getImageRaw(EFontGlyphType::Color, idx), llformat("%s_%d_%d_%d_clr.png", mFTFace->family_name, (int)(mPointSize * 10), mStyle, idx));
- }
-}
-
-const LLFontBitmapCache* LLFontFreetype::getFontBitmapCache() const
-{
- return mFontBitmapCachep;
-}
-
-void LLFontFreetype::setStyle(U8 style)
-{
- mStyle = style;
-}
-
-U8 LLFontFreetype::getStyle() const
-{
- return mStyle;
-}
-
-bool LLFontFreetype::setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U16 height, const U8* data, U32 stride) const
-{
- LLImageRaw* image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Color, bitmap_num);
- llassert(!mIsFallback);
- llassert(image_raw && (image_raw->getComponents() == 4));
-
- // NOTE: inspired by LLImageRaw::setSubImage()
- U32* image_data = (U32*)image_raw->getData();
- if (!image_data)
- {
- return false;
- }
-
- for (U32 idxRow = 0; idxRow < height; idxRow++)
- {
- const U32 nSrcRow = height - 1 - idxRow;
- const U32 nSrcOffset = nSrcRow * width * image_raw->getComponents();
- const U32 nDstOffset = (y + idxRow) * image_raw->getWidth() + x;
-
- for (U32 idxCol = 0; idxCol < width; idxCol++)
- {
- U32 nTemp = nSrcOffset + idxCol * 4;
- image_data[nDstOffset + idxCol] = data[nTemp + 3] << 24 | data[nTemp] << 16 | data[nTemp + 1] << 8 | data[nTemp + 2];
- }
- }
-
- return true;
-}
-
-void LLFontFreetype::setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride) const
-{
- LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Grayscale, bitmap_num);
- LLImageDataLock lock(image_raw);
-
- llassert(!mIsFallback);
- llassert(image_raw && (image_raw->getComponents() == 2));
-
- U8 *target = image_raw->getData();
- llassert(target);
-
- if (!data || !target)
- {
- 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++;
- }
- }
-}
-
+/**
+ * @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>
+#ifdef LL_WINDOWS
+#include <freetype2\freetype\ftsystem.h>
+#endif
+#include "llfontfreetypesvg.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 "lldir.h"
+#include "llerror.h"
+#include "llimage.h"
+#include "llimagepng.h"
+//#include "llimagej2c.h"
+#include "llmath.h" // Linden math
+#include "llstring.h"
+//#include "imdebug.h"
+#include "llfontbitmapcache.h"
+#include "llgl.h"
+
+#define ENABLE_OT_SVG_SUPPORT
+
+FT_Render_Mode gFontRenderMode = FT_RENDER_MODE_NORMAL;
+
+LLFontManager *gFontManagerp = NULL;
+
+FT_Library gFTLibrary = NULL;
+
+//static
+void LLFontManager::initClass()
+{
+ if (!gFontManagerp)
+ {
+ 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.
+ LL_ERRS() << "Freetype initialization failure!" << LL_ENDL;
+ FT_Done_FreeType(gFTLibrary);
+ }
+
+#ifdef ENABLE_OT_SVG_SUPPORT
+ SVG_RendererHooks hooks = {
+ LLFontFreeTypeSvgRenderer::OnInit,
+ LLFontFreeTypeSvgRenderer::OnFree,
+ LLFontFreeTypeSvgRenderer::OnRender,
+ LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot,
+ };
+ FT_Property_Set(gFTLibrary, "ot-svg", "svg-hooks", &hooks);
+#endif
+}
+
+LLFontManager::~LLFontManager()
+{
+ FT_Done_FreeType(gFTLibrary);
+}
+
+
+LLFontGlyphInfo::LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type)
+: mGlyphIndex(index),
+ mGlyphType(glyph_type),
+ 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
+ mBitmapEntry(std::make_pair(EFontGlyphType::Unspecified, -1)) // Which bitmap in the bitmap cache contains this glyph
+{
+}
+
+LLFontGlyphInfo::LLFontGlyphInfo(const LLFontGlyphInfo& fgi)
+ : mGlyphIndex(fgi.mGlyphIndex)
+ , mGlyphType(fgi.mGlyphType)
+ , mWidth(fgi.mWidth)
+ , mHeight(fgi.mHeight)
+ , mXAdvance(fgi.mXAdvance)
+ , mYAdvance(fgi.mYAdvance)
+ , mXBitmapOffset(fgi.mXBitmapOffset)
+ , mYBitmapOffset(fgi.mYBitmapOffset)
+ , mXBearing(fgi.mXBearing)
+ , mYBearing(fgi.mYBearing)
+{
+ mBitmapEntry = fgi.mBitmapEntry;
+}
+
+LLFontFreetype::LLFontFreetype()
+: mFontBitmapCachep(new LLFontBitmapCache),
+ mAscender(0.f),
+ mDescender(0.f),
+ mLineHeight(0.f),
+#ifdef LL_WINDOWS
+ pFileStream(NULL),
+ pFtStream(NULL),
+#endif
+ 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());
+ mCharGlyphInfoMap.clear();
+
+#ifdef LL_WINDOWS
+ delete pFileStream; // closed by FT_Done_Face
+ delete pFtStream;
+#endif
+ delete mFontBitmapCachep;
+ // mFallbackFonts cleaned up by LLPointer destructor
+}
+
+#ifdef LL_WINDOWS
+unsigned long ft_read_cb(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count) {
+ if (count <= 0) return count;
+ llifstream *file_stream = static_cast<llifstream *>(stream->descriptor.pointer);
+ file_stream->seekg(offset, std::ios::beg);
+ file_stream->read((char*)buffer, count);
+ return file_stream->gcount();
+}
+
+void ft_close_cb(FT_Stream stream) {
+ llifstream *file_stream = static_cast<llifstream *>(stream->descriptor.pointer);
+ file_stream->close();
+}
+#endif
+
+bool LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, bool is_fallback, S32 face_n)
+{
+ // 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;
+#ifdef LL_WINDOWS
+ error = ftOpenFace(filename, face_n);
+#else
+ error = FT_New_Face( gFTLibrary,
+ filename.c_str(),
+ 0,
+ &mFTFace);
+#endif
+
+ if (error)
+ {
+#ifdef LL_WINDOWS
+ clearFontStreams();
+#endif
+ 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);
+#ifdef LL_WINDOWS
+ clearFontStreams();
+#endif
+ 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 = ll_round(0.5f + (x_max - x_min));
+ S32 max_char_height = ll_round(0.5f + (y_max - y_min));
+
+ mFontBitmapCachep->init(max_char_width, max_char_height);
+
+ if (!mFTFace->charmap)
+ {
+ //LL_INFOS() << " no unicode encoding, set whatever encoding there is..." << LL_ENDL;
+ FT_Set_Charmap(mFTFace, mFTFace->charmaps[0]);
+ }
+
+ if (!mIsFallback)
+ {
+ // Add the default glyph
+ addGlyphFromFont(this, 0, 0, EFontGlyphType::Grayscale);
+ }
+
+ mName = filename;
+ mPointSize = point_size;
+
+ mStyle = LLFontGL::NORMAL;
+ if(mFTFace->style_flags & FT_STYLE_FLAG_BOLD)
+ {
+ mStyle |= LLFontGL::BOLD;
+ }
+
+ if(mFTFace->style_flags & FT_STYLE_FLAG_ITALIC)
+ {
+ mStyle |= LLFontGL::ITALIC;
+ }
+
+ return true;
+}
+
+S32 LLFontFreetype::getNumFaces(const std::string& filename)
+{
+ if (mFTFace)
+ {
+ FT_Done_Face(mFTFace);
+ mFTFace = NULL;
+ }
+
+ S32 num_faces = 1;
+
+#ifdef LL_WINDOWS
+ int error = ftOpenFace(filename, 0);
+
+ if (error)
+ {
+ return 0;
+ }
+ else
+ {
+ num_faces = mFTFace->num_faces;
+ }
+
+ FT_Done_Face(mFTFace);
+ clearFontStreams();
+ mFTFace = NULL;
+#endif
+
+ return num_faces;
+}
+
+#ifdef LL_WINDOWS
+S32 LLFontFreetype::ftOpenFace(const std::string& filename, S32 face_n)
+{
+ S32 error = -1;
+ pFileStream = new llifstream(filename, std::ios::binary);
+ if (pFileStream->is_open())
+ {
+ std::streampos beg = pFileStream->tellg();
+ pFileStream->seekg(0, std::ios::end);
+ std::streampos end = pFileStream->tellg();
+ std::size_t file_size = end - beg;
+ pFileStream->seekg(0, std::ios::beg);
+
+ pFtStream = new LLFT_Stream();
+ pFtStream->base = 0;
+ pFtStream->pos = 0;
+ pFtStream->size = file_size;
+ pFtStream->descriptor.pointer = pFileStream;
+ pFtStream->read = ft_read_cb;
+ pFtStream->close = ft_close_cb;
+
+ FT_Open_Args args;
+ args.flags = FT_OPEN_STREAM;
+ args.stream = (FT_StreamRec*)pFtStream;
+ error = FT_Open_Face(gFTLibrary, &args, face_n, &mFTFace);
+ }
+ return error;
+}
+
+void LLFontFreetype::clearFontStreams()
+{
+ if (pFileStream)
+ {
+ pFileStream->close();
+ }
+ delete pFileStream;
+ delete pFtStream;
+ pFileStream = NULL;
+ pFtStream = NULL;
+}
+#endif
+
+void LLFontFreetype::addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font,
+ const char_functor_t& functor)
+{
+ mFallbackFonts.emplace_back(fallback_font, functor);
+}
+
+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, EFontGlyphType::Unspecified);
+ 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, EFontGlyphType::Unspecified);;
+ U32 left_glyph = left_glyph_info ? left_glyph_info->mGlyphIndex : 0;
+ // Kern this puppy.
+ LLFontGlyphInfo* right_glyph_info = getGlyphInfo(char_right, EFontGlyphType::Unspecified);
+ 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, EFontGlyphType glyph_type) const
+{
+ if (!mFTFace)
+ {
+ return NULL;
+ }
+
+ llassert(!mIsFallback);
+ llassert(glyph_type < EFontGlyphType::Count);
+ //LL_DEBUGS() << "Adding new glyph for " << wch << " to font" << LL_ENDL;
+
+ // Initialize char to glyph map
+ FT_UInt glyph_index = FT_Get_Char_Index(mFTFace, wch);
+ if (glyph_index == 0)
+ {
+ // No corresponding glyph in this font: look for a glyph in fallback
+ // fonts.
+ size_t count = mFallbackFonts.size();
+ if (LLStringOps::isEmoji(wch))
+ {
+ // This is a "genuine" emoji (in the range 0x1f000-0x20000): print
+ // it using the emoji font(s) if possible. HB
+ for (size_t i = 0; i < count; ++i)
+ {
+ const fallback_font_t& pair = mFallbackFonts[i];
+ if (!pair.second || !pair.second(wch))
+ {
+ // If this font does not have a functor, or the character
+ // does not pass the functor, reject it. Note: we keep the
+ // functor test (despite the fact we already tested for
+ // LLStringOps::isEmoji(wch) above), in case we would use
+ // different, more restrictive or partionned functors in
+ // the future with several different emoji fonts. HB
+ continue;
+ }
+ glyph_index = FT_Get_Char_Index(pair.first->mFTFace, wch);
+ if (glyph_index)
+ {
+ return addGlyphFromFont(pair.first, wch, glyph_index,
+ glyph_type);
+ }
+ }
+ }
+ // Then try and find a monochrome fallback font that could print this
+ // glyph: such fonts do *not* have a functor. We give priority to
+ // monochrome fonts for non-genuine emojis so that UI elements which
+ // used to render with them before the emojis font introduction (e.g.
+ // check marks in menus, or LSL dialogs text and buttons) do render the
+ // same way as they always did. HB
+ std::vector<size_t> emoji_fonts_idx;
+ for (size_t i = 0; i < count; ++i)
+ {
+ const fallback_font_t& pair = mFallbackFonts[i];
+ if (pair.second)
+ {
+ // If this font got a functor, remember the index for later and
+ // try the next fallback font. HB
+ emoji_fonts_idx.push_back(i);
+ continue;
+ }
+ glyph_index = FT_Get_Char_Index(pair.first->mFTFace, wch);
+ if (glyph_index)
+ {
+ return addGlyphFromFont(pair.first, wch, glyph_index,
+ glyph_type);
+ }
+ }
+ // Everything failed so far: this character is not a genuine emoji,
+ // neither a special character known from our monochrome fallback
+ // fonts: make a last try, using the emoji font(s), but ignoring the
+ // functor to render using whatever (colorful) glyph that might be
+ // available in such fonts for this character. HB
+ for (size_t j = 0, count2 = emoji_fonts_idx.size(); j < count2; ++j)
+ {
+ const fallback_font_t& pair = mFallbackFonts[emoji_fonts_idx[j]];
+ glyph_index = FT_Get_Char_Index(pair.first->mFTFace, wch);
+ if (glyph_index)
+ {
+ return addGlyphFromFont(pair.first, wch, glyph_index,
+ glyph_type);
+ }
+ }
+ }
+
+ auto range_it = mCharGlyphInfoMap.equal_range(wch);
+ char_glyph_info_map_t::iterator iter =
+ std::find_if(range_it.first, range_it.second,
+ [&glyph_type](const char_glyph_info_map_t::value_type& entry)
+ {
+ return entry.second->mGlyphType == glyph_type;
+ });
+ if (iter == range_it.second)
+ {
+ return addGlyphFromFont(this, wch, glyph_index, glyph_type);
+ }
+ return NULL;
+}
+
+LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index, EFontGlyphType requested_glyph_type) const
+{
+ LL_PROFILE_ZONE_SCOPED;
+ if (mFTFace == NULL)
+ return NULL;
+
+ llassert(!mIsFallback);
+ fontp->renderGlyph(requested_glyph_type, glyph_index);
+
+ EFontGlyphType bitmap_glyph_type = EFontGlyphType::Unspecified;
+ switch (fontp->mFTFace->glyph->bitmap.pixel_mode)
+ {
+ case FT_PIXEL_MODE_MONO:
+ case FT_PIXEL_MODE_GRAY:
+ bitmap_glyph_type = EFontGlyphType::Grayscale;
+ break;
+ case FT_PIXEL_MODE_BGRA:
+ bitmap_glyph_type = EFontGlyphType::Color;
+ break;
+ default:
+ llassert_always(true);
+ break;
+ }
+ S32 width = fontp->mFTFace->glyph->bitmap.width;
+ S32 height = fontp->mFTFace->glyph->bitmap.rows;
+
+ S32 pos_x, pos_y;
+ U32 bitmap_num;
+ mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_glyph_type, bitmap_num);
+ mAddGlyphCount++;
+
+ LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index, requested_glyph_type);
+ gi->mXBitmapOffset = pos_x;
+ gi->mYBitmapOffset = pos_y;
+ gi->mBitmapEntry = std::make_pair(bitmap_glyph_type, 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);
+
+ if (requested_glyph_type != bitmap_glyph_type)
+ {
+ LLFontGlyphInfo* gi_temp = new LLFontGlyphInfo(*gi);
+ gi_temp->mGlyphType = bitmap_glyph_type;
+ insertGlyphInfo(wch, gi_temp);
+ }
+
+ 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;
+ }
+
+ setSubImageLuminanceAlpha(pos_x,
+ pos_y,
+ bitmap_num,
+ width,
+ height,
+ buffer_data,
+ buffer_row_stride);
+
+ if (tmp_graydata)
+ delete[] tmp_graydata;
+ }
+ else if (fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)
+ {
+ setSubImageBGRA(pos_x,
+ pos_y,
+ bitmap_num,
+ fontp->mFTFace->glyph->bitmap.width,
+ fontp->mFTFace->glyph->bitmap.rows,
+ fontp->mFTFace->glyph->bitmap.buffer,
+ llabs(fontp->mFTFace->glyph->bitmap.pitch));
+ } else {
+ llassert(false);
+ }
+
+ LLImageGL *image_gl = mFontBitmapCachep->getImageGL(bitmap_glyph_type, bitmap_num);
+ LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_glyph_type, bitmap_num);
+ image_gl->setSubImage(image_raw, 0, 0, image_gl->getWidth(), image_gl->getHeight());
+
+ return gi;
+}
+
+LLFontGlyphInfo* LLFontFreetype::getGlyphInfo(llwchar wch, EFontGlyphType glyph_type) const
+{
+ std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch);
+
+ char_glyph_info_map_t::iterator iter = (EFontGlyphType::Unspecified != glyph_type)
+ ? std::find_if(range_it.first, range_it.second, [&glyph_type](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == glyph_type; })
+ : range_it.first;
+ if (iter != range_it.second)
+ {
+ return iter->second;
+ }
+ else
+ {
+ // this glyph doesn't yet exist, so render it and return the result
+ return addGlyph(wch, (EFontGlyphType::Unspecified != glyph_type) ? glyph_type : EFontGlyphType::Grayscale);
+ }
+}
+
+void LLFontFreetype::insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const
+{
+ llassert(gi->mGlyphType < EFontGlyphType::Count);
+ std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch);
+
+ char_glyph_info_map_t::iterator iter =
+ std::find_if(range_it.first, range_it.second, [&gi](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == gi->mGlyphType; });
+ if (iter != range_it.second)
+ {
+ delete iter->second;
+ iter->second = gi;
+ }
+ else
+ {
+ mCharGlyphInfoMap.insert(std::make_pair(wch, gi));
+ }
+}
+
+void LLFontFreetype::renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index) const
+{
+ if (mFTFace == NULL)
+ return;
+
+ FT_Int32 load_flags = FT_LOAD_FORCE_AUTOHINT;
+ if (EFontGlyphType::Color == bitmap_type)
+ {
+ // We may not actually get a color render so our caller should always examine mFTFace->glyph->bitmap.pixel_mode
+ load_flags |= FT_LOAD_COLOR;
+ }
+
+ FT_Error error = FT_Load_Glyph(mFTFace, glyph_index, load_flags);
+ if (FT_Err_Ok != error)
+ {
+ std::string message = llformat(
+ "Error %d (%s) loading glyph %u: bitmap_type=%u, load_flags=%d",
+ error, FT_Error_String(error), glyph_index, bitmap_type, load_flags);
+ LL_WARNS_ONCE() << message << LL_ENDL;
+ error = FT_Load_Glyph(mFTFace, glyph_index, load_flags ^ FT_LOAD_COLOR);
+ llassert_always_msg(FT_Err_Ok == error, message.c_str());
+ }
+
+ llassert_always(! FT_Render_Glyph(mFTFace->glyph, gFontRenderMode) );
+
+ mRenderGlyphCount++;
+}
+
+void LLFontFreetype::reset(F32 vert_dpi, F32 horz_dpi)
+{
+ resetBitmapCache();
+ loadFace(mName, mPointSize, vert_dpi ,horz_dpi, mIsFallback, 0);
+ if (!mIsFallback)
+ {
+ // This is the head of the list - need to rebuild ourself and all fallbacks.
+ if (mFallbackFonts.empty())
+ {
+ LL_WARNS() << "LLFontGL::reset(), no fallback fonts present" << LL_ENDL;
+ }
+ else
+ {
+ for (fallback_font_vector_t::iterator it = mFallbackFonts.begin(); it != mFallbackFonts.end(); ++it)
+ {
+ it->first->reset(vert_dpi, horz_dpi);
+ }
+ }
+ }
+}
+
+void LLFontFreetype::resetBitmapCache()
+{
+ for (char_glyph_info_map_t::iterator it = mCharGlyphInfoMap.begin(), end_it = mCharGlyphInfoMap.end();
+ it != end_it;
+ ++it)
+ {
+ delete it->second;
+ }
+ 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, EFontGlyphType::Grayscale);
+ }
+}
+
+void LLFontFreetype::destroyGL()
+{
+ mFontBitmapCachep->destroyGL();
+}
+
+const std::string &LLFontFreetype::getName() const
+{
+ return mName;
+}
+
+static void dumpFontBitmap(const LLImageRaw* image_raw, const std::string& file_name)
+{
+ LLPointer<LLImagePNG> tmpImage = new LLImagePNG();
+ if ( (tmpImage->encode(image_raw, 0.0f)) && (tmpImage->save(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, file_name))) )
+ {
+ LL_INFOS("Font") << "Successfully saved " << file_name << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("Font") << "Failed to save " << file_name << LL_ENDL;
+ }
+}
+
+void LLFontFreetype::dumpFontBitmaps() const
+{
+ // Dump all the regular bitmaps (if any)
+ for (int idx = 0, cnt = mFontBitmapCachep->getNumBitmaps(EFontGlyphType::Grayscale); idx < cnt; idx++)
+ {
+ dumpFontBitmap(mFontBitmapCachep->getImageRaw(EFontGlyphType::Grayscale, idx), llformat("%s_%d_%d_%d.png", mFTFace->family_name, (int)(mPointSize * 10), mStyle, idx));
+ }
+
+ // Dump all the color bitmaps (if any)
+ for (int idx = 0, cnt = mFontBitmapCachep->getNumBitmaps(EFontGlyphType::Color); idx < cnt; idx++)
+ {
+ dumpFontBitmap(mFontBitmapCachep->getImageRaw(EFontGlyphType::Color, idx), llformat("%s_%d_%d_%d_clr.png", mFTFace->family_name, (int)(mPointSize * 10), mStyle, idx));
+ }
+}
+
+const LLFontBitmapCache* LLFontFreetype::getFontBitmapCache() const
+{
+ return mFontBitmapCachep;
+}
+
+void LLFontFreetype::setStyle(U8 style)
+{
+ mStyle = style;
+}
+
+U8 LLFontFreetype::getStyle() const
+{
+ return mStyle;
+}
+
+bool LLFontFreetype::setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U16 height, const U8* data, U32 stride) const
+{
+ LLImageRaw* image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Color, bitmap_num);
+ llassert(!mIsFallback);
+ llassert(image_raw && (image_raw->getComponents() == 4));
+
+ // NOTE: inspired by LLImageRaw::setSubImage()
+ U32* image_data = (U32*)image_raw->getData();
+ if (!image_data)
+ {
+ return false;
+ }
+
+ for (U32 idxRow = 0; idxRow < height; idxRow++)
+ {
+ const U32 nSrcRow = height - 1 - idxRow;
+ const U32 nSrcOffset = nSrcRow * width * image_raw->getComponents();
+ const U32 nDstOffset = (y + idxRow) * image_raw->getWidth() + x;
+
+ for (U32 idxCol = 0; idxCol < width; idxCol++)
+ {
+ U32 nTemp = nSrcOffset + idxCol * 4;
+ image_data[nDstOffset + idxCol] = data[nTemp + 3] << 24 | data[nTemp] << 16 | data[nTemp + 1] << 8 | data[nTemp + 2];
+ }
+ }
+
+ return true;
+}
+
+void LLFontFreetype::setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride) const
+{
+ LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Grayscale, bitmap_num);
+ LLImageDataLock lock(image_raw);
+
+ llassert(!mIsFallback);
+ llassert(image_raw && (image_raw->getComponents() == 2));
+
+ U8 *target = image_raw->getData();
+ llassert(target);
+
+ if (!data || !target)
+ {
+ 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
index 8ba3c50d30..eba89f5def 100644
--- a/indra/llrender/llfontfreetype.h
+++ b/indra/llrender/llfontfreetype.h
@@ -1,193 +1,193 @@
-/**
- * @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;
-struct FT_StreamRec_;
-typedef struct FT_StreamRec_ LLFT_Stream;
-
-class LLFontManager
-{
-public:
- static void initClass();
- static void cleanupClass();
-
-private:
- LLFontManager();
- ~LLFontManager();
-};
-
-struct LLFontGlyphInfo
-{
- LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type);
- LLFontGlyphInfo(const LLFontGlyphInfo& fgi);
-
- U32 mGlyphIndex;
- EFontGlyphType mGlyphType;
-
- // 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
- std::pair<EFontGlyphType, S32> mBitmapEntry; // 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, bool is_fallback, S32 face_n);
-
- S32 getNumFaces(const std::string& filename);
-
-#ifdef LL_WINDOWS
- S32 ftOpenFace(const std::string& filename, S32 face_n);
- void clearFontStreams();
-#endif
-
- typedef std::function<bool(llwchar)> char_functor_t;
- void addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor = nullptr);
-
- // 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, EFontGlyphType glyph_type) const;
-
- void reset(F32 vert_dpi, F32 horz_dpi);
-
- void destroyGL();
-
- const std::string& getName() const;
-
- void dumpFontBitmaps() const;
- const 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 setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U16 height, const U8* data, U32 stride) const;
- bool hasGlyph(llwchar wch) const; // Has a glyph for this character
- LLFontGlyphInfo* addGlyph(llwchar wch, EFontGlyphType glyph_type) const; // Add a new character to the font if necessary
- LLFontGlyphInfo* addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index, EFontGlyphType bitmap_type) const; // Add a glyph from this font to the other (returns the glyph_index, 0 if not found)
- void renderGlyph(EFontGlyphType bitmap_type, 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;
-
-#ifdef LL_WINDOWS
- llifstream *pFileStream;
- LLFT_Stream *pFtStream;
-#endif
-
- bool mIsFallback;
- typedef std::pair<LLPointer<LLFontFreetype>, char_functor_t> fallback_font_t;
- typedef std::vector<fallback_font_t> fallback_font_vector_t;
- fallback_font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars)
-
- // *NOTE: the same glyph can be present with multiple representations (but the pointer is always unique)
- typedef boost::unordered_multimap<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t;
- mutable char_glyph_info_map_t mCharGlyphInfoMap; // Information about glyph location in bitmap
-
- mutable LLFontBitmapCache* mFontBitmapCachep;
-
- mutable S32 mRenderGlyphCount;
- mutable S32 mAddGlyphCount;
-};
-
-#endif // LL_FONTFREETYPE_H
+/**
+ * @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;
+struct FT_StreamRec_;
+typedef struct FT_StreamRec_ LLFT_Stream;
+
+class LLFontManager
+{
+public:
+ static void initClass();
+ static void cleanupClass();
+
+private:
+ LLFontManager();
+ ~LLFontManager();
+};
+
+struct LLFontGlyphInfo
+{
+ LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type);
+ LLFontGlyphInfo(const LLFontGlyphInfo& fgi);
+
+ U32 mGlyphIndex;
+ EFontGlyphType mGlyphType;
+
+ // 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
+ std::pair<EFontGlyphType, S32> mBitmapEntry; // 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, bool is_fallback, S32 face_n);
+
+ S32 getNumFaces(const std::string& filename);
+
+#ifdef LL_WINDOWS
+ S32 ftOpenFace(const std::string& filename, S32 face_n);
+ void clearFontStreams();
+#endif
+
+ typedef std::function<bool(llwchar)> char_functor_t;
+ void addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor = nullptr);
+
+ // 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, EFontGlyphType glyph_type) const;
+
+ void reset(F32 vert_dpi, F32 horz_dpi);
+
+ void destroyGL();
+
+ const std::string& getName() const;
+
+ void dumpFontBitmaps() const;
+ const 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 setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U16 height, const U8* data, U32 stride) const;
+ bool hasGlyph(llwchar wch) const; // Has a glyph for this character
+ LLFontGlyphInfo* addGlyph(llwchar wch, EFontGlyphType glyph_type) const; // Add a new character to the font if necessary
+ LLFontGlyphInfo* addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index, EFontGlyphType bitmap_type) const; // Add a glyph from this font to the other (returns the glyph_index, 0 if not found)
+ void renderGlyph(EFontGlyphType bitmap_type, 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;
+
+#ifdef LL_WINDOWS
+ llifstream *pFileStream;
+ LLFT_Stream *pFtStream;
+#endif
+
+ bool mIsFallback;
+ typedef std::pair<LLPointer<LLFontFreetype>, char_functor_t> fallback_font_t;
+ typedef std::vector<fallback_font_t> fallback_font_vector_t;
+ fallback_font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars)
+
+ // *NOTE: the same glyph can be present with multiple representations (but the pointer is always unique)
+ typedef boost::unordered_multimap<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t;
+ mutable char_glyph_info_map_t mCharGlyphInfoMap; // Information about glyph location in bitmap
+
+ mutable LLFontBitmapCache* mFontBitmapCachep;
+
+ mutable S32 mRenderGlyphCount;
+ mutable S32 mAddGlyphCount;
+};
+
+#endif // LL_FONTFREETYPE_H
diff --git a/indra/llrender/llfontfreetypesvg.h b/indra/llrender/llfontfreetypesvg.h
index 510f3f66e9..4170cab273 100644
--- a/indra/llrender/llfontfreetypesvg.h
+++ b/indra/llrender/llfontfreetypesvg.h
@@ -1,54 +1,54 @@
-/**
- * @file llfontfreetypesvg.h
- * @brief Freetype font library SVG glyph rendering
- *
- * $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$
- */
-
-#pragma once
-
-#include <ft2build.h>
-#include FT_TYPES_H
-#include FT_MODULE_H
-#include FT_OTSVG_H
-
- // See https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html
-class LLFontFreeTypeSvgRenderer
-{
-public:
- // Called when the very first OT-SVG glyph is rendered (across the entire lifetime of our FT_Library object)
- static FT_Error OnInit(FT_Pointer* state);
-
- // Called when the ot-svg module is being freed (but only called if the init hook was called previously)
- static void OnFree(FT_Pointer* state);
-
- // Called to preset the glyph slot, twice per glyph:
- // - when FT_Load_Glyph needs to preset the glyph slot (with cache == false)
- // - right before the svg module calls the render callback hook. (with cache == true)
- static FT_Error OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, FT_Bool cache, FT_Pointer* state);
-
- // Called to render an OT-SVG glyph (right after the preset hook OnPresetGlypthSlot was called with cache set to true)
- static FT_Error OnRender(FT_GlyphSlot glyph_slot, FT_Pointer* state);
-
- // Called to deallocate our per glyph slot data
- static void OnDataFinalizer(void* objectp);
-};
+/**
+ * @file llfontfreetypesvg.h
+ * @brief Freetype font library SVG glyph rendering
+ *
+ * $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$
+ */
+
+#pragma once
+
+#include <ft2build.h>
+#include FT_TYPES_H
+#include FT_MODULE_H
+#include FT_OTSVG_H
+
+ // See https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html
+class LLFontFreeTypeSvgRenderer
+{
+public:
+ // Called when the very first OT-SVG glyph is rendered (across the entire lifetime of our FT_Library object)
+ static FT_Error OnInit(FT_Pointer* state);
+
+ // Called when the ot-svg module is being freed (but only called if the init hook was called previously)
+ static void OnFree(FT_Pointer* state);
+
+ // Called to preset the glyph slot, twice per glyph:
+ // - when FT_Load_Glyph needs to preset the glyph slot (with cache == false)
+ // - right before the svg module calls the render callback hook. (with cache == true)
+ static FT_Error OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, FT_Bool cache, FT_Pointer* state);
+
+ // Called to render an OT-SVG glyph (right after the preset hook OnPresetGlypthSlot was called with cache set to true)
+ static FT_Error OnRender(FT_GlyphSlot glyph_slot, FT_Pointer* state);
+
+ // Called to deallocate our per glyph slot data
+ static void OnDataFinalizer(void* objectp);
+};
diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp
index f8edfe56d9..25706c77eb 100644
--- a/indra/llrender/llfontgl.cpp
+++ b/indra/llrender/llfontgl.cpp
@@ -1,1320 +1,1320 @@
-/**
- * @file llfontgl.cpp
- * @brief Wrapper around FreeType
- *
- * $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 "llfontgl.h"
-
-// Linden library includes
-#include "llfasttimer.h"
-#include "llfontfreetype.h"
-#include "llfontbitmapcache.h"
-#include "llfontregistry.h"
-#include "llgl.h"
-#include "llimagegl.h"
-#include "llrender.h"
-#include "llstl.h"
-#include "v4color.h"
-#include "lltexture.h"
-#include "lldir.h"
-#include "llstring.h"
-
-// Third party library includes
-#include <boost/tokenizer.hpp>
-
-#if LL_WINDOWS
-#include <Shlobj.h>
-#include <Knownfolders.h>
-#include <Objbase.h>
-#endif // LL_WINDOWS
-
-const S32 BOLD_OFFSET = 1;
-
-// static class members
-F32 LLFontGL::sVertDPI = 96.f;
-F32 LLFontGL::sHorizDPI = 96.f;
-F32 LLFontGL::sScaleX = 1.f;
-F32 LLFontGL::sScaleY = 1.f;
-bool LLFontGL::sDisplayFont = true ;
-std::string LLFontGL::sAppDir;
-
-LLColor4 LLFontGL::sShadowColor(0.f, 0.f, 0.f, 1.f);
-LLFontRegistry* LLFontGL::sFontRegistry = NULL;
-
-LLCoordGL LLFontGL::sCurOrigin;
-F32 LLFontGL::sCurDepth;
-std::vector<std::pair<LLCoordGL, F32> > LLFontGL::sOriginStack;
-
-const F32 PAD_UVY = 0.5f; // half of vertical padding between glyphs in the glyph texture
-const F32 DROP_SHADOW_SOFT_STRENGTH = 0.3f;
-
-LLFontGL::LLFontGL()
-{
-}
-
-LLFontGL::~LLFontGL()
-{
-}
-
-void LLFontGL::reset()
-{
- mFontFreetype->reset(sVertDPI, sHorizDPI);
-}
-
-void LLFontGL::destroyGL()
-{
- mFontFreetype->destroyGL();
-}
-
-bool LLFontGL::loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n)
-{
- if(mFontFreetype == reinterpret_cast<LLFontFreetype*>(NULL))
- {
- mFontFreetype = new LLFontFreetype;
- }
-
- return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, is_fallback, face_n);
-}
-
-S32 LLFontGL::getNumFaces(const std::string& filename)
-{
- if (mFontFreetype == reinterpret_cast<LLFontFreetype*>(NULL))
- {
- mFontFreetype = new LLFontFreetype;
- }
-
- return mFontFreetype->getNumFaces(filename);
-}
-
-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, bool use_color) const
-{
- LLRectf rect_float(rect.mLeft, rect.mTop, rect.mRight, rect.mBottom);
- return render(wstr, begin_offset, rect_float, color, halign, valign, style, shadow, max_chars, right_x, use_ellipses, use_color);
-}
-
-S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRectf& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style,
- ShadowType shadow, S32 max_chars, F32* right_x, bool use_ellipses, bool use_color) const
-{
- F32 x = rect.mLeft;
- F32 y = 0.f;
-
- switch(valign)
- {
- 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;
- }
- return render(wstr, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, rect.getWidth(), right_x, use_ellipses, use_color);
-}
-
-
-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, bool use_color) const
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
-
- if(!sDisplayFont) //do not display texts
- {
- return wstr.length() ;
- }
-
- 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);
-
- // 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 (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)
- {
- shadow = NO_SHADOW;
- }
- }
-
- gGL.pushUIMatrix();
-
- gGL.loadUIIdentity();
-
- LLVector2 origin(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY));
-
- // Depth translation, so that floating text appears 'in-world'
- // and is correctly occluded.
- gGL.translatef(0.f,0.f,sCurDepth);
-
- S32 chars_drawn = 0;
- S32 i;
- S32 length;
-
- if (-1 == max_chars)
- {
- max_chars = length = (S32)wstr.length() - begin_offset;
- }
- else
- {
- length = llmin((S32)wstr.length() - begin_offset, max_chars );
- }
-
- F32 cur_x, cur_y, cur_render_x, cur_render_y;
-
- // Not guaranteed to be set correctly
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
-
- cur_x = ((F32)x * sScaleX) + origin.mV[VX];
- cur_y = ((F32)y * sScaleY) + origin.mV[VY];
-
- // Offset y by vertical alignment.
- // use unscaled font metrics here
- switch (valign)
- {
- case TOP:
- cur_y -= llceil(mFontFreetype->getAscenderHeight());
- break;
- case BOTTOM:
- cur_y += llceil(mFontFreetype->getDescenderHeight());
- break;
- case VCENTER:
- cur_y -= llceil((llceil(mFontFreetype->getAscenderHeight()) - llceil(mFontFreetype->getDescenderHeight())) / 2.f);
- break;
- case BASELINE:
- // Baseline, do nothing.
- break;
- default:
- break;
- }
-
- switch (halign)
- {
- case LEFT:
- break;
- case RIGHT:
- cur_x -= llmin(scaled_max_pixels, ll_round(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX));
- break;
- case HCENTER:
- cur_x -= llmin(scaled_max_pixels, ll_round(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)) / 2;
- break;
- default:
- break;
- }
-
- cur_render_y = cur_y;
- cur_render_x = cur_x;
-
- F32 start_x = (F32)ll_round(cur_x);
-
- const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache();
-
- F32 inv_width = 1.f / font_bitmap_cache->getBitmapWidth();
- F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight();
-
- const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL;
-
- bool draw_ellipses = false;
- if (use_ellipses)
- {
- // check for too long of a string
- S32 string_width = ll_round(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(std::string("....")));
- scaled_max_pixels = llmax(0, scaled_max_pixels - ll_round(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);
- // Preserve the transparency to render fading emojis in fading text (e.g.
- // for the chat console)... HB
- LLColor4U emoji_color(255, 255, 255, text_color.mV[VW]);
-
- std::pair<EFontGlyphType, S32> bitmap_entry = std::make_pair(EFontGlyphType::Grayscale, -1);
- S32 glyph_count = 0;
- for (i = begin_offset; i < begin_offset + length; i++)
- {
- llwchar wch = wstr[i];
-
- const LLFontGlyphInfo* fgi = next_glyph;
- next_glyph = NULL;
- if(!fgi)
- {
- fgi = mFontFreetype->getGlyphInfo(wch, (!use_color) ? EFontGlyphType::Grayscale : EFontGlyphType::Color);
- }
- if (!fgi)
- {
- LL_ERRS() << "Missing Glyph Info" << LL_ENDL;
- break;
- }
- // Per-glyph bitmap texture.
- std::pair<EFontGlyphType, S32> next_bitmap_entry = fgi->mBitmapEntry;
- if (next_bitmap_entry != bitmap_entry)
- {
- // Actually draw the queued glyphs before switching their texture;
- // otherwise the queued glyphs will be taken from wrong textures.
- if (glyph_count > 0)
- {
- gGL.begin(LLRender::QUADS);
- {
- gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4);
- }
- gGL.end();
- glyph_count = 0;
- }
-
- bitmap_entry = next_bitmap_entry;
- LLImageGL* font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second);
- 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;
- }
-
- // 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((F32)ll_round(cur_render_x + (F32)fgi->mXBearing),
- (F32)ll_round(cur_render_y + (F32)fgi->mYBearing),
- (F32)ll_round(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth,
- (F32)ll_round(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight);
-
- if (glyph_count >= GLYPH_BATCH_SIZE)
- {
- gGL.begin(LLRender::QUADS);
- {
- gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4);
- }
- gGL.end();
-
- glyph_count = 0;
- }
-
- const LLColor4U& col =
- bitmap_entry.first == EFontGlyphType::Grayscale ? text_color
- : emoji_color;
- drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect,
- col, style_to_add, shadow, 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.
- next_glyph = mFontFreetype->getGlyphInfo(next_char, (!use_color) ? EFontGlyphType::Grayscale : EFontGlyphType::Color);
- cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
- }
-
- // 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)ll_round(cur_x);
- //cur_y = (F32)ll_round(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 - origin.mV[VX]) / sScaleX;
- }
-
- //FIXME: add underline as glyph?
- if (style_to_add & UNDERLINE)
- {
- F32 descender = (F32)llfloor(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();
- }
-
- if (draw_ellipses)
- {
-
- // recursively render ellipses at end of string
- // we've already reserved enough room
- gGL.pushUIMatrix();
- renderUTF8(std::string("..."),
- 0,
- (cur_x - origin.mV[VX]) / sScaleX, (F32)y,
- color,
- LEFT, valign,
- style_to_add,
- shadow,
- S32_MAX, max_pixels,
- right_x,
- false,
- use_color);
- gGL.popUIMatrix();
- }
-
- gGL.popUIMatrix();
-
- return chars_drawn;
-}
-
-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 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, bool use_color) const
-{
- return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color);
-}
-
-S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const
-{
- return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW);
-}
-
-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);
-}
-
-// font metrics - override for LLFontFreetype that returns units of virtual pixels
-F32 LLFontGL::getAscenderHeight() const
-{
- return mFontFreetype->getAscenderHeight() / sScaleY;
-}
-
-F32 LLFontGL::getDescenderHeight() const
-{
- return mFontFreetype->getDescenderHeight() / sScaleY;
-}
-
-S32 LLFontGL::getLineHeight() const
-{
- return llceil(mFontFreetype->getAscenderHeight() / sScaleY) + llceil(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);
-}
-
-S32 LLFontGL::getWidth(const llwchar* wchars) const
-{
- return getWidth(wchars, 0, S32_MAX);
-}
-
-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, S32 begin_offset, S32 max_chars) const
-{
- F32 width = getWidthF32(wchars, begin_offset, max_chars);
- return ll_round(width);
-}
-
-F32 LLFontGL::getWidthF32(const std::string& utf8text) const
-{
- LLWString wtext = utf8str_to_wstring(utf8text);
- return getWidthF32(wtext.c_str(), 0, S32_MAX);
-}
-
-F32 LLFontGL::getWidthF32(const llwchar* wchars) const
-{
- return getWidthF32(wchars, 0, S32_MAX);
-}
-
-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, S32 begin_offset, S32 max_chars, bool no_padding) const
-{
- const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL;
-
- F32 cur_x = 0;
- const S32 max_index = begin_offset + max_chars;
-
- const LLFontGlyphInfo* next_glyph = NULL;
-
- F32 width_padding = 0.f;
- for (S32 i = begin_offset; i < max_index && wchars[i] != 0; i++)
- {
- llwchar wch = wchars[i];
-
- const LLFontGlyphInfo* fgi = next_glyph;
- next_glyph = NULL;
- if(!fgi)
- {
- fgi = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);
- }
-
- F32 advance = mFontFreetype->getXAdvance(fgi);
-
- if (!no_padding)
- {
- // 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, EFontGlyphType::Unspecified);
- cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
- }
- // Round after kerning.
- cur_x = (F32)ll_round(cur_x);
- }
-
- if (!no_padding)
- {
- // add in extra pixels for last character's width past its xadvance
- cur_x += width_padding;
- }
-
- return cur_x / sScaleX;
-}
-
-void LLFontGL::generateASCIIglyphs()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI
- for (U32 i = 32; (i < 127); i++)
- {
- mFontFreetype->getGlyphInfo(i, EFontGlyphType::Grayscale);
- }
-}
-
-// 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, EWordWrapStyle end_on_word_boundary) const
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI
- if (!wchars || !wchars[0] || max_chars == 0)
- {
- return 0;
- }
-
- llassert(max_pixels >= 0.f);
- llassert(max_chars >= 0);
-
- bool clip = false;
- F32 cur_x = 0;
-
- S32 start_of_last_word = 0;
- bool in_word = false;
-
- // avoid S32 overflow when max_pixels == S32_MAX by staying in floating point
- F32 scaled_max_pixels = max_pixels * sScaleX;
- F32 width_padding = 0.f;
-
- LLFontGlyphInfo* next_glyph = NULL;
-
- S32 i;
- for (i=0; (i < max_chars); i++)
- {
- llwchar wch = wchars[i];
-
- if(wch == 0)
- {
- // Null terminator. We're done.
- break;
- }
-
- if (in_word)
- {
- if (iswspace(wch))
- {
- if(wch !=(0x00A0))
- {
- in_word = false;
- }
- }
- if (iswindividual(wch))
- {
- if (iswpunct(wchars[i+1]))
- {
- in_word=true;
- }
- else
- {
- in_word=false;
- start_of_last_word = i;
- }
- }
- }
- else
- {
- start_of_last_word = i;
- if (!iswspace(wch)||!iswindividual(wch))
- {
- in_word = true;
- }
- }
-
- LLFontGlyphInfo* fgi = next_glyph;
- next_glyph = NULL;
- if(!fgi)
- {
- fgi = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);
-
- if (NULL == fgi)
- {
- return 0;
- }
- }
-
- // 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], EFontGlyphType::Unspecified);
- cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
- }
-
- // Round after kerning.
- cur_x = (F32)ll_round(cur_x);
- }
-
- if( clip )
- {
- 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)
- {
- return 0;
- }
-
- F32 total_width = 0.0;
- S32 drawable_chars = 0;
-
- F32 scaled_max_pixels = max_pixels * sScaleX;
-
- S32 start = llmin(start_pos, text_len - 1);
- for (S32 i = start; i >= 0; i--)
- {
- llwchar wch = wchars[i];
-
- const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);
-
- // 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
-
- if( scaled_max_pixels < (total_width + width) )
- {
- break;
- }
-
- total_width += width;
- drawable_chars++;
-
- if( max_chars >= 0 && drawable_chars >= max_chars )
- {
- break;
- }
-
- if ( i > 0 )
- {
- // kerning
- total_width += mFontFreetype->getXKerning(wchars[i-1], wch);
- }
-
- // Round after kerning.
- total_width = (F32)ll_round(total_width);
- }
-
- 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, S32 begin_offset, F32 target_x, F32 max_pixels, S32 max_chars, bool round) const
-{
- if (!wchars || !wchars[0] || max_chars == 0)
- {
- return 0;
- }
-
- F32 cur_x = 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 - 1);
-
- F32 scaled_max_pixels = max_pixels * sScaleX;
-
- const LLFontGlyphInfo* next_glyph = NULL;
-
- S32 pos;
- for (pos = begin_offset; pos < max_index; pos++)
- {
- llwchar wch = wchars[pos];
- if (!wch)
- {
- break; // done
- }
-
- const LLFontGlyphInfo* glyph = next_glyph;
- next_glyph = NULL;
- if(!glyph)
- {
- glyph = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);
- }
-
- F32 char_width = mFontFreetype->getXAdvance(glyph);
-
- 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;
- }
-
- cur_x += char_width;
-
- if (((pos + 1) < max_index)
- && (wchars[(pos + 1)]))
- {
- // Kern this puppy.
- next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1], EFontGlyphType::Unspecified);
- cur_x += mFontFreetype->getXKerning(glyph, next_glyph);
- }
-
-
- // Round after kerning.
- cur_x = (F32)ll_round(cur_x);
- }
-
- return llmin(max_chars, pos - begin_offset);
-}
-
-const LLFontDescriptor& LLFontGL::getFontDesc() const
-{
- return mFontDescriptor;
-}
-
-// static
-void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures)
-{
- sVertDPI = (F32)llfloor(screen_dpi * y_scale);
- sHorizDPI = (F32)llfloor(screen_dpi * x_scale);
- sScaleX = x_scale;
- sScaleY = y_scale;
- sAppDir = app_dir;
-
- // Font registry init
- if (!sFontRegistry)
- {
- sFontRegistry = new LLFontRegistry(create_gl_textures);
- sFontRegistry->parseFontInfo("fonts.xml");
- }
- else
- {
- sFontRegistry->reset();
- }
-
- LLFontGL::loadDefaultFonts();
-}
-
-void LLFontGL::dumpTextures()
-{
- if (mFontFreetype.notNull())
- {
- mFontFreetype->dumpFontBitmaps();
- }
-}
-
-// static
-void LLFontGL::dumpFonts()
-{
- sFontRegistry->dump();
-}
-
-// static
-void LLFontGL::dumpFontTextures()
-{
- sFontRegistry->dumpTextures();
-}
-
-// 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()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI
- bool succ = true;
- succ &= (NULL != getFontSansSerifSmall());
- succ &= (NULL != getFontSansSerif());
- succ &= (NULL != getFontSansSerifBig());
- succ &= (NULL != getFontSansSerifHuge());
- succ &= (NULL != getFontSansSerifBold());
- succ &= (NULL != getFontMonospace());
- return succ;
-}
-
-void LLFontGL::loadCommonFonts()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI
- getFont(LLFontDescriptor("SansSerif", "Small", BOLD));
- getFont(LLFontDescriptor("SansSerif", "Large", BOLD));
- getFont(LLFontDescriptor("SansSerif", "Huge", BOLD));
- getFont(LLFontDescriptor("Monospace", "Medium", 0));
-}
-
-// static
-void LLFontGL::destroyDefaultFonts()
-{
- // Remove the actual fonts.
- delete sFontRegistry;
- sFontRegistry = NULL;
-}
-
-//static
-void LLFontGL::destroyAllGL()
-{
- if (sFontRegistry)
- {
- sFontRegistry->destroyGL();
- }
-}
-
-// static
-U8 LLFontGL::getStyleFromString(const std::string &style)
-{
- S32 ret = 0;
- if (style.find("BOLD") != style.npos)
- {
- ret |= BOLD;
- }
- if (style.find("ITALIC") != style.npos)
- {
- ret |= ITALIC;
- }
- if (style.find("UNDERLINE") != style.npos)
- {
- ret |= UNDERLINE;
- }
- return ret;
-}
-
-// static
-std::string LLFontGL::getStringFromStyle(U8 style)
-{
- std::string style_string;
- if (style == NORMAL)
- {
- style_string += "|NORMAL";
- }
- if (style & BOLD)
- {
- style_string += "|BOLD";
- }
- if (style & ITALIC)
- {
- style_string += "|ITALIC";
- }
- if (style & UNDERLINE)
- {
- style_string += "|UNDERLINE";
- }
- 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
-std::string LLFontGL::nameFromHAlign(LLFontGL::HAlign align)
-{
- 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 std::string& name)
-{
- LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT;
- if (name == "left")
- {
- gl_hfont_align = LLFontGL::LEFT;
- }
- else if (name == "right")
- {
- gl_hfont_align = LLFontGL::RIGHT;
- }
- else if (name == "center")
- {
- gl_hfont_align = LLFontGL::HCENTER;
- }
- //else leave left
- return gl_hfont_align;
-}
-
-// static
-std::string LLFontGL::nameFromVAlign(LLFontGL::VAlign align)
-{
- 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 std::string& name)
-{
- LLFontGL::VAlign gl_vfont_align = LLFontGL::BASELINE;
- if (name == "top")
- {
- gl_vfont_align = LLFontGL::TOP;
- }
- else if (name == "center")
- {
- gl_vfont_align = LLFontGL::VCENTER;
- }
- else if (name == "baseline")
- {
- gl_vfont_align = LLFontGL::BASELINE;
- }
- else if (name == "bottom")
- {
- gl_vfont_align = LLFontGL::BOTTOM;
- }
- //else leave baseline
- return gl_vfont_align;
-}
-
-//static
-LLFontGL* LLFontGL::getFontEmojiSmall()
-{
- static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Small", 0));
- return fontp;;
-}
-
-//static
-LLFontGL* LLFontGL::getFontEmojiMedium()
-{
- static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Medium", 0));
- return fontp;;
-}
-
-//static
-LLFontGL* LLFontGL::getFontEmojiLarge()
-{
- static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Large", 0));
- return fontp;;
-}
-
-//static
-LLFontGL* LLFontGL::getFontEmojiHuge()
-{
- static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Huge", 0));
- return fontp;;
-}
-
-//static
-LLFontGL* LLFontGL::getFontMonospace()
-{
- static LLFontGL* fontp = getFont(LLFontDescriptor("Monospace","Monospace",0));
- return fontp;
-}
-
-//static
-LLFontGL* LLFontGL::getFontSansSerifSmall()
-{
- static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Small",0));
- return fontp;
-}
-
-//static
-LLFontGL* LLFontGL::getFontSansSerifSmallBold()
-{
- static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Small",BOLD));
- return fontp;
-}
-
-//static
-LLFontGL* LLFontGL::getFontSansSerifSmallItalic()
-{
- static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Small",ITALIC));
- return fontp;
-}
-
-//static
-LLFontGL* LLFontGL::getFontSansSerif()
-{
- static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Medium",0));
- return fontp;
-}
-
-//static
-LLFontGL* LLFontGL::getFontSansSerifBig()
-{
- static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Large",0));
- return fontp;
-}
-
-//static
-LLFontGL* LLFontGL::getFontSansSerifHuge()
-{
- static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Huge",0));
- return fontp;
-}
-
-//static
-LLFontGL* LLFontGL::getFontSansSerifBold()
-{
- static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Medium",BOLD));
- return fontp;
-}
-
-//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()
-{
-#if LL_DARWIN
- // HACK for macOS
- return "/System/Library/Fonts/";
-
-#elif LL_WINDOWS
- auto system_root = LLStringUtil::getenv("SystemRoot");
- if (! system_root.empty())
- {
- std::string fontpath(gDirUtilp->add(system_root, "fonts") + gDirUtilp->getDirDelimiter());
- LL_INFOS() << "from SystemRoot: " << fontpath << LL_ENDL;
- return fontpath;
- }
-
- wchar_t *pwstr = NULL;
- HRESULT okay = SHGetKnownFolderPath(FOLDERID_Fonts, 0, NULL, &pwstr);
- if (SUCCEEDED(okay) && pwstr)
- {
- std::string fontpath(ll_convert_wide_to_string(pwstr));
- // SHGetKnownFolderPath() contract requires us to free pwstr
- CoTaskMemFree(pwstr);
- LL_INFOS() << "from SHGetKnownFolderPath(): " << fontpath << LL_ENDL;
- return fontpath;
- }
-#endif
-
- LL_WARNS() << "Could not determine system fonts path" << LL_ENDL;
- return {};
-}
-
-
-// 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)
-{
- LL_ERRS() << "Not implemented!" << LL_ENDL;
-}
-
-LLFontGL &LLFontGL::operator=(const LLFontGL &source)
-{
- LL_ERRS() << "Not implemented" << LL_ENDL;
- 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(screen_rect.mRight, screen_rect.mTop, 0.f);
- uv_out[index] = LLVector2(uv_rect.mRight, uv_rect.mTop);
- colors_out[index] = color;
- index++;
-
- vertex_out[index] = LLVector3(screen_rect.mLeft, screen_rect.mTop, 0.f);
- uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mTop);
- colors_out[index] = color;
- index++;
-
- vertex_out[index] = LLVector3(screen_rect.mLeft, screen_rect.mBottom, 0.f);
- uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mBottom);
- colors_out[index] = color;
- index++;
-
- vertex_out[index] = LLVector3(screen_rect.mRight, 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++;
- }
-}
+/**
+ * @file llfontgl.cpp
+ * @brief Wrapper around FreeType
+ *
+ * $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 "llfontgl.h"
+
+// Linden library includes
+#include "llfasttimer.h"
+#include "llfontfreetype.h"
+#include "llfontbitmapcache.h"
+#include "llfontregistry.h"
+#include "llgl.h"
+#include "llimagegl.h"
+#include "llrender.h"
+#include "llstl.h"
+#include "v4color.h"
+#include "lltexture.h"
+#include "lldir.h"
+#include "llstring.h"
+
+// Third party library includes
+#include <boost/tokenizer.hpp>
+
+#if LL_WINDOWS
+#include <Shlobj.h>
+#include <Knownfolders.h>
+#include <Objbase.h>
+#endif // LL_WINDOWS
+
+const S32 BOLD_OFFSET = 1;
+
+// static class members
+F32 LLFontGL::sVertDPI = 96.f;
+F32 LLFontGL::sHorizDPI = 96.f;
+F32 LLFontGL::sScaleX = 1.f;
+F32 LLFontGL::sScaleY = 1.f;
+bool LLFontGL::sDisplayFont = true ;
+std::string LLFontGL::sAppDir;
+
+LLColor4 LLFontGL::sShadowColor(0.f, 0.f, 0.f, 1.f);
+LLFontRegistry* LLFontGL::sFontRegistry = NULL;
+
+LLCoordGL LLFontGL::sCurOrigin;
+F32 LLFontGL::sCurDepth;
+std::vector<std::pair<LLCoordGL, F32> > LLFontGL::sOriginStack;
+
+const F32 PAD_UVY = 0.5f; // half of vertical padding between glyphs in the glyph texture
+const F32 DROP_SHADOW_SOFT_STRENGTH = 0.3f;
+
+LLFontGL::LLFontGL()
+{
+}
+
+LLFontGL::~LLFontGL()
+{
+}
+
+void LLFontGL::reset()
+{
+ mFontFreetype->reset(sVertDPI, sHorizDPI);
+}
+
+void LLFontGL::destroyGL()
+{
+ mFontFreetype->destroyGL();
+}
+
+bool LLFontGL::loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n)
+{
+ if(mFontFreetype == reinterpret_cast<LLFontFreetype*>(NULL))
+ {
+ mFontFreetype = new LLFontFreetype;
+ }
+
+ return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, is_fallback, face_n);
+}
+
+S32 LLFontGL::getNumFaces(const std::string& filename)
+{
+ if (mFontFreetype == reinterpret_cast<LLFontFreetype*>(NULL))
+ {
+ mFontFreetype = new LLFontFreetype;
+ }
+
+ return mFontFreetype->getNumFaces(filename);
+}
+
+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, bool use_color) const
+{
+ LLRectf rect_float(rect.mLeft, rect.mTop, rect.mRight, rect.mBottom);
+ return render(wstr, begin_offset, rect_float, color, halign, valign, style, shadow, max_chars, right_x, use_ellipses, use_color);
+}
+
+S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRectf& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style,
+ ShadowType shadow, S32 max_chars, F32* right_x, bool use_ellipses, bool use_color) const
+{
+ F32 x = rect.mLeft;
+ F32 y = 0.f;
+
+ switch(valign)
+ {
+ 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;
+ }
+ return render(wstr, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, rect.getWidth(), right_x, use_ellipses, use_color);
+}
+
+
+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, bool use_color) const
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ if(!sDisplayFont) //do not display texts
+ {
+ return wstr.length() ;
+ }
+
+ 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);
+
+ // 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 (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)
+ {
+ shadow = NO_SHADOW;
+ }
+ }
+
+ gGL.pushUIMatrix();
+
+ gGL.loadUIIdentity();
+
+ LLVector2 origin(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY));
+
+ // Depth translation, so that floating text appears 'in-world'
+ // and is correctly occluded.
+ gGL.translatef(0.f,0.f,sCurDepth);
+
+ S32 chars_drawn = 0;
+ S32 i;
+ S32 length;
+
+ if (-1 == max_chars)
+ {
+ max_chars = length = (S32)wstr.length() - begin_offset;
+ }
+ else
+ {
+ length = llmin((S32)wstr.length() - begin_offset, max_chars );
+ }
+
+ F32 cur_x, cur_y, cur_render_x, cur_render_y;
+
+ // Not guaranteed to be set correctly
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+
+ cur_x = ((F32)x * sScaleX) + origin.mV[VX];
+ cur_y = ((F32)y * sScaleY) + origin.mV[VY];
+
+ // Offset y by vertical alignment.
+ // use unscaled font metrics here
+ switch (valign)
+ {
+ case TOP:
+ cur_y -= llceil(mFontFreetype->getAscenderHeight());
+ break;
+ case BOTTOM:
+ cur_y += llceil(mFontFreetype->getDescenderHeight());
+ break;
+ case VCENTER:
+ cur_y -= llceil((llceil(mFontFreetype->getAscenderHeight()) - llceil(mFontFreetype->getDescenderHeight())) / 2.f);
+ break;
+ case BASELINE:
+ // Baseline, do nothing.
+ break;
+ default:
+ break;
+ }
+
+ switch (halign)
+ {
+ case LEFT:
+ break;
+ case RIGHT:
+ cur_x -= llmin(scaled_max_pixels, ll_round(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX));
+ break;
+ case HCENTER:
+ cur_x -= llmin(scaled_max_pixels, ll_round(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)) / 2;
+ break;
+ default:
+ break;
+ }
+
+ cur_render_y = cur_y;
+ cur_render_x = cur_x;
+
+ F32 start_x = (F32)ll_round(cur_x);
+
+ const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache();
+
+ F32 inv_width = 1.f / font_bitmap_cache->getBitmapWidth();
+ F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight();
+
+ const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL;
+
+ bool draw_ellipses = false;
+ if (use_ellipses)
+ {
+ // check for too long of a string
+ S32 string_width = ll_round(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(std::string("....")));
+ scaled_max_pixels = llmax(0, scaled_max_pixels - ll_round(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);
+ // Preserve the transparency to render fading emojis in fading text (e.g.
+ // for the chat console)... HB
+ LLColor4U emoji_color(255, 255, 255, text_color.mV[VW]);
+
+ std::pair<EFontGlyphType, S32> bitmap_entry = std::make_pair(EFontGlyphType::Grayscale, -1);
+ S32 glyph_count = 0;
+ for (i = begin_offset; i < begin_offset + length; i++)
+ {
+ llwchar wch = wstr[i];
+
+ const LLFontGlyphInfo* fgi = next_glyph;
+ next_glyph = NULL;
+ if(!fgi)
+ {
+ fgi = mFontFreetype->getGlyphInfo(wch, (!use_color) ? EFontGlyphType::Grayscale : EFontGlyphType::Color);
+ }
+ if (!fgi)
+ {
+ LL_ERRS() << "Missing Glyph Info" << LL_ENDL;
+ break;
+ }
+ // Per-glyph bitmap texture.
+ std::pair<EFontGlyphType, S32> next_bitmap_entry = fgi->mBitmapEntry;
+ if (next_bitmap_entry != bitmap_entry)
+ {
+ // Actually draw the queued glyphs before switching their texture;
+ // otherwise the queued glyphs will be taken from wrong textures.
+ if (glyph_count > 0)
+ {
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4);
+ }
+ gGL.end();
+ glyph_count = 0;
+ }
+
+ bitmap_entry = next_bitmap_entry;
+ LLImageGL* font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second);
+ 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;
+ }
+
+ // 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((F32)ll_round(cur_render_x + (F32)fgi->mXBearing),
+ (F32)ll_round(cur_render_y + (F32)fgi->mYBearing),
+ (F32)ll_round(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth,
+ (F32)ll_round(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight);
+
+ if (glyph_count >= GLYPH_BATCH_SIZE)
+ {
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4);
+ }
+ gGL.end();
+
+ glyph_count = 0;
+ }
+
+ const LLColor4U& col =
+ bitmap_entry.first == EFontGlyphType::Grayscale ? text_color
+ : emoji_color;
+ drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect,
+ col, style_to_add, shadow, 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.
+ next_glyph = mFontFreetype->getGlyphInfo(next_char, (!use_color) ? EFontGlyphType::Grayscale : EFontGlyphType::Color);
+ cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
+ }
+
+ // 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)ll_round(cur_x);
+ //cur_y = (F32)ll_round(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 - origin.mV[VX]) / sScaleX;
+ }
+
+ //FIXME: add underline as glyph?
+ if (style_to_add & UNDERLINE)
+ {
+ F32 descender = (F32)llfloor(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();
+ }
+
+ if (draw_ellipses)
+ {
+
+ // recursively render ellipses at end of string
+ // we've already reserved enough room
+ gGL.pushUIMatrix();
+ renderUTF8(std::string("..."),
+ 0,
+ (cur_x - origin.mV[VX]) / sScaleX, (F32)y,
+ color,
+ LEFT, valign,
+ style_to_add,
+ shadow,
+ S32_MAX, max_pixels,
+ right_x,
+ false,
+ use_color);
+ gGL.popUIMatrix();
+ }
+
+ gGL.popUIMatrix();
+
+ return chars_drawn;
+}
+
+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 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, bool use_color) const
+{
+ return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color);
+}
+
+S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const
+{
+ return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW);
+}
+
+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);
+}
+
+// font metrics - override for LLFontFreetype that returns units of virtual pixels
+F32 LLFontGL::getAscenderHeight() const
+{
+ return mFontFreetype->getAscenderHeight() / sScaleY;
+}
+
+F32 LLFontGL::getDescenderHeight() const
+{
+ return mFontFreetype->getDescenderHeight() / sScaleY;
+}
+
+S32 LLFontGL::getLineHeight() const
+{
+ return llceil(mFontFreetype->getAscenderHeight() / sScaleY) + llceil(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);
+}
+
+S32 LLFontGL::getWidth(const llwchar* wchars) const
+{
+ return getWidth(wchars, 0, S32_MAX);
+}
+
+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, S32 begin_offset, S32 max_chars) const
+{
+ F32 width = getWidthF32(wchars, begin_offset, max_chars);
+ return ll_round(width);
+}
+
+F32 LLFontGL::getWidthF32(const std::string& utf8text) const
+{
+ LLWString wtext = utf8str_to_wstring(utf8text);
+ return getWidthF32(wtext.c_str(), 0, S32_MAX);
+}
+
+F32 LLFontGL::getWidthF32(const llwchar* wchars) const
+{
+ return getWidthF32(wchars, 0, S32_MAX);
+}
+
+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, S32 begin_offset, S32 max_chars, bool no_padding) const
+{
+ const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL;
+
+ F32 cur_x = 0;
+ const S32 max_index = begin_offset + max_chars;
+
+ const LLFontGlyphInfo* next_glyph = NULL;
+
+ F32 width_padding = 0.f;
+ for (S32 i = begin_offset; i < max_index && wchars[i] != 0; i++)
+ {
+ llwchar wch = wchars[i];
+
+ const LLFontGlyphInfo* fgi = next_glyph;
+ next_glyph = NULL;
+ if(!fgi)
+ {
+ fgi = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);
+ }
+
+ F32 advance = mFontFreetype->getXAdvance(fgi);
+
+ if (!no_padding)
+ {
+ // 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, EFontGlyphType::Unspecified);
+ cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
+ }
+ // Round after kerning.
+ cur_x = (F32)ll_round(cur_x);
+ }
+
+ if (!no_padding)
+ {
+ // add in extra pixels for last character's width past its xadvance
+ cur_x += width_padding;
+ }
+
+ return cur_x / sScaleX;
+}
+
+void LLFontGL::generateASCIIglyphs()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI
+ for (U32 i = 32; (i < 127); i++)
+ {
+ mFontFreetype->getGlyphInfo(i, EFontGlyphType::Grayscale);
+ }
+}
+
+// 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, EWordWrapStyle end_on_word_boundary) const
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI
+ if (!wchars || !wchars[0] || max_chars == 0)
+ {
+ return 0;
+ }
+
+ llassert(max_pixels >= 0.f);
+ llassert(max_chars >= 0);
+
+ bool clip = false;
+ F32 cur_x = 0;
+
+ S32 start_of_last_word = 0;
+ bool in_word = false;
+
+ // avoid S32 overflow when max_pixels == S32_MAX by staying in floating point
+ F32 scaled_max_pixels = max_pixels * sScaleX;
+ F32 width_padding = 0.f;
+
+ LLFontGlyphInfo* next_glyph = NULL;
+
+ S32 i;
+ for (i=0; (i < max_chars); i++)
+ {
+ llwchar wch = wchars[i];
+
+ if(wch == 0)
+ {
+ // Null terminator. We're done.
+ break;
+ }
+
+ if (in_word)
+ {
+ if (iswspace(wch))
+ {
+ if(wch !=(0x00A0))
+ {
+ in_word = false;
+ }
+ }
+ if (iswindividual(wch))
+ {
+ if (iswpunct(wchars[i+1]))
+ {
+ in_word=true;
+ }
+ else
+ {
+ in_word=false;
+ start_of_last_word = i;
+ }
+ }
+ }
+ else
+ {
+ start_of_last_word = i;
+ if (!iswspace(wch)||!iswindividual(wch))
+ {
+ in_word = true;
+ }
+ }
+
+ LLFontGlyphInfo* fgi = next_glyph;
+ next_glyph = NULL;
+ if(!fgi)
+ {
+ fgi = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);
+
+ if (NULL == fgi)
+ {
+ return 0;
+ }
+ }
+
+ // 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], EFontGlyphType::Unspecified);
+ cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
+ }
+
+ // Round after kerning.
+ cur_x = (F32)ll_round(cur_x);
+ }
+
+ if( clip )
+ {
+ 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)
+ {
+ return 0;
+ }
+
+ F32 total_width = 0.0;
+ S32 drawable_chars = 0;
+
+ F32 scaled_max_pixels = max_pixels * sScaleX;
+
+ S32 start = llmin(start_pos, text_len - 1);
+ for (S32 i = start; i >= 0; i--)
+ {
+ llwchar wch = wchars[i];
+
+ const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);
+
+ // 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
+
+ if( scaled_max_pixels < (total_width + width) )
+ {
+ break;
+ }
+
+ total_width += width;
+ drawable_chars++;
+
+ if( max_chars >= 0 && drawable_chars >= max_chars )
+ {
+ break;
+ }
+
+ if ( i > 0 )
+ {
+ // kerning
+ total_width += mFontFreetype->getXKerning(wchars[i-1], wch);
+ }
+
+ // Round after kerning.
+ total_width = (F32)ll_round(total_width);
+ }
+
+ 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, S32 begin_offset, F32 target_x, F32 max_pixels, S32 max_chars, bool round) const
+{
+ if (!wchars || !wchars[0] || max_chars == 0)
+ {
+ return 0;
+ }
+
+ F32 cur_x = 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 - 1);
+
+ F32 scaled_max_pixels = max_pixels * sScaleX;
+
+ const LLFontGlyphInfo* next_glyph = NULL;
+
+ S32 pos;
+ for (pos = begin_offset; pos < max_index; pos++)
+ {
+ llwchar wch = wchars[pos];
+ if (!wch)
+ {
+ break; // done
+ }
+
+ const LLFontGlyphInfo* glyph = next_glyph;
+ next_glyph = NULL;
+ if(!glyph)
+ {
+ glyph = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);
+ }
+
+ F32 char_width = mFontFreetype->getXAdvance(glyph);
+
+ 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;
+ }
+
+ cur_x += char_width;
+
+ if (((pos + 1) < max_index)
+ && (wchars[(pos + 1)]))
+ {
+ // Kern this puppy.
+ next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1], EFontGlyphType::Unspecified);
+ cur_x += mFontFreetype->getXKerning(glyph, next_glyph);
+ }
+
+
+ // Round after kerning.
+ cur_x = (F32)ll_round(cur_x);
+ }
+
+ return llmin(max_chars, pos - begin_offset);
+}
+
+const LLFontDescriptor& LLFontGL::getFontDesc() const
+{
+ return mFontDescriptor;
+}
+
+// static
+void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures)
+{
+ sVertDPI = (F32)llfloor(screen_dpi * y_scale);
+ sHorizDPI = (F32)llfloor(screen_dpi * x_scale);
+ sScaleX = x_scale;
+ sScaleY = y_scale;
+ sAppDir = app_dir;
+
+ // Font registry init
+ if (!sFontRegistry)
+ {
+ sFontRegistry = new LLFontRegistry(create_gl_textures);
+ sFontRegistry->parseFontInfo("fonts.xml");
+ }
+ else
+ {
+ sFontRegistry->reset();
+ }
+
+ LLFontGL::loadDefaultFonts();
+}
+
+void LLFontGL::dumpTextures()
+{
+ if (mFontFreetype.notNull())
+ {
+ mFontFreetype->dumpFontBitmaps();
+ }
+}
+
+// static
+void LLFontGL::dumpFonts()
+{
+ sFontRegistry->dump();
+}
+
+// static
+void LLFontGL::dumpFontTextures()
+{
+ sFontRegistry->dumpTextures();
+}
+
+// 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()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI
+ bool succ = true;
+ succ &= (NULL != getFontSansSerifSmall());
+ succ &= (NULL != getFontSansSerif());
+ succ &= (NULL != getFontSansSerifBig());
+ succ &= (NULL != getFontSansSerifHuge());
+ succ &= (NULL != getFontSansSerifBold());
+ succ &= (NULL != getFontMonospace());
+ return succ;
+}
+
+void LLFontGL::loadCommonFonts()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI
+ getFont(LLFontDescriptor("SansSerif", "Small", BOLD));
+ getFont(LLFontDescriptor("SansSerif", "Large", BOLD));
+ getFont(LLFontDescriptor("SansSerif", "Huge", BOLD));
+ getFont(LLFontDescriptor("Monospace", "Medium", 0));
+}
+
+// static
+void LLFontGL::destroyDefaultFonts()
+{
+ // Remove the actual fonts.
+ delete sFontRegistry;
+ sFontRegistry = NULL;
+}
+
+//static
+void LLFontGL::destroyAllGL()
+{
+ if (sFontRegistry)
+ {
+ sFontRegistry->destroyGL();
+ }
+}
+
+// static
+U8 LLFontGL::getStyleFromString(const std::string &style)
+{
+ S32 ret = 0;
+ if (style.find("BOLD") != style.npos)
+ {
+ ret |= BOLD;
+ }
+ if (style.find("ITALIC") != style.npos)
+ {
+ ret |= ITALIC;
+ }
+ if (style.find("UNDERLINE") != style.npos)
+ {
+ ret |= UNDERLINE;
+ }
+ return ret;
+}
+
+// static
+std::string LLFontGL::getStringFromStyle(U8 style)
+{
+ std::string style_string;
+ if (style == NORMAL)
+ {
+ style_string += "|NORMAL";
+ }
+ if (style & BOLD)
+ {
+ style_string += "|BOLD";
+ }
+ if (style & ITALIC)
+ {
+ style_string += "|ITALIC";
+ }
+ if (style & UNDERLINE)
+ {
+ style_string += "|UNDERLINE";
+ }
+ 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
+std::string LLFontGL::nameFromHAlign(LLFontGL::HAlign align)
+{
+ 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 std::string& name)
+{
+ LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT;
+ if (name == "left")
+ {
+ gl_hfont_align = LLFontGL::LEFT;
+ }
+ else if (name == "right")
+ {
+ gl_hfont_align = LLFontGL::RIGHT;
+ }
+ else if (name == "center")
+ {
+ gl_hfont_align = LLFontGL::HCENTER;
+ }
+ //else leave left
+ return gl_hfont_align;
+}
+
+// static
+std::string LLFontGL::nameFromVAlign(LLFontGL::VAlign align)
+{
+ 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 std::string& name)
+{
+ LLFontGL::VAlign gl_vfont_align = LLFontGL::BASELINE;
+ if (name == "top")
+ {
+ gl_vfont_align = LLFontGL::TOP;
+ }
+ else if (name == "center")
+ {
+ gl_vfont_align = LLFontGL::VCENTER;
+ }
+ else if (name == "baseline")
+ {
+ gl_vfont_align = LLFontGL::BASELINE;
+ }
+ else if (name == "bottom")
+ {
+ gl_vfont_align = LLFontGL::BOTTOM;
+ }
+ //else leave baseline
+ return gl_vfont_align;
+}
+
+//static
+LLFontGL* LLFontGL::getFontEmojiSmall()
+{
+ static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Small", 0));
+ return fontp;;
+}
+
+//static
+LLFontGL* LLFontGL::getFontEmojiMedium()
+{
+ static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Medium", 0));
+ return fontp;;
+}
+
+//static
+LLFontGL* LLFontGL::getFontEmojiLarge()
+{
+ static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Large", 0));
+ return fontp;;
+}
+
+//static
+LLFontGL* LLFontGL::getFontEmojiHuge()
+{
+ static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Huge", 0));
+ return fontp;;
+}
+
+//static
+LLFontGL* LLFontGL::getFontMonospace()
+{
+ static LLFontGL* fontp = getFont(LLFontDescriptor("Monospace","Monospace",0));
+ return fontp;
+}
+
+//static
+LLFontGL* LLFontGL::getFontSansSerifSmall()
+{
+ static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Small",0));
+ return fontp;
+}
+
+//static
+LLFontGL* LLFontGL::getFontSansSerifSmallBold()
+{
+ static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Small",BOLD));
+ return fontp;
+}
+
+//static
+LLFontGL* LLFontGL::getFontSansSerifSmallItalic()
+{
+ static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Small",ITALIC));
+ return fontp;
+}
+
+//static
+LLFontGL* LLFontGL::getFontSansSerif()
+{
+ static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Medium",0));
+ return fontp;
+}
+
+//static
+LLFontGL* LLFontGL::getFontSansSerifBig()
+{
+ static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Large",0));
+ return fontp;
+}
+
+//static
+LLFontGL* LLFontGL::getFontSansSerifHuge()
+{
+ static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Huge",0));
+ return fontp;
+}
+
+//static
+LLFontGL* LLFontGL::getFontSansSerifBold()
+{
+ static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Medium",BOLD));
+ return fontp;
+}
+
+//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()
+{
+#if LL_DARWIN
+ // HACK for macOS
+ return "/System/Library/Fonts/";
+
+#elif LL_WINDOWS
+ auto system_root = LLStringUtil::getenv("SystemRoot");
+ if (! system_root.empty())
+ {
+ std::string fontpath(gDirUtilp->add(system_root, "fonts") + gDirUtilp->getDirDelimiter());
+ LL_INFOS() << "from SystemRoot: " << fontpath << LL_ENDL;
+ return fontpath;
+ }
+
+ wchar_t *pwstr = NULL;
+ HRESULT okay = SHGetKnownFolderPath(FOLDERID_Fonts, 0, NULL, &pwstr);
+ if (SUCCEEDED(okay) && pwstr)
+ {
+ std::string fontpath(ll_convert_wide_to_string(pwstr));
+ // SHGetKnownFolderPath() contract requires us to free pwstr
+ CoTaskMemFree(pwstr);
+ LL_INFOS() << "from SHGetKnownFolderPath(): " << fontpath << LL_ENDL;
+ return fontpath;
+ }
+#endif
+
+ LL_WARNS() << "Could not determine system fonts path" << LL_ENDL;
+ return {};
+}
+
+
+// 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)
+{
+ LL_ERRS() << "Not implemented!" << LL_ENDL;
+}
+
+LLFontGL &LLFontGL::operator=(const LLFontGL &source)
+{
+ LL_ERRS() << "Not implemented" << LL_ENDL;
+ 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(screen_rect.mRight, screen_rect.mTop, 0.f);
+ uv_out[index] = LLVector2(uv_rect.mRight, uv_rect.mTop);
+ colors_out[index] = color;
+ index++;
+
+ vertex_out[index] = LLVector3(screen_rect.mLeft, screen_rect.mTop, 0.f);
+ uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mTop);
+ colors_out[index] = color;
+ index++;
+
+ vertex_out[index] = LLVector3(screen_rect.mLeft, screen_rect.mBottom, 0.f);
+ uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mBottom);
+ colors_out[index] = color;
+ index++;
+
+ vertex_out[index] = LLVector3(screen_rect.mRight, 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 e3588ed183..de7529a583 100644
--- a/indra/llrender/llfontgl.h
+++ b/indra/llrender/llfontgl.h
@@ -1,248 +1,248 @@
-/**
- * @file llfontgl.h
- * @author Doug Soo
- * @brief Wrapper around FreeType
- *
- * $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_LLFONTGL_H
-#define LL_LLFONTGL_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;
-
-// Structure used to store previously requested fonts.
-class LLFontRegistry;
-
-class LLFontGL
-{
-public:
- enum HAlign
- {
- // Horizontal location of x,y coord to render.
- LEFT = 0, // Left align
- RIGHT = 1, // Right align
- HCENTER = 2, // Center
- };
-
- enum VAlign
- {
- // Vertical location of x,y coord to render.
- TOP = 3, // Top align
- VCENTER = 4, // Center
- BASELINE = 5, // Baseline
- BOTTOM = 6 // Bottom
- };
-
- enum StyleFlags
- {
- // text style to render. May be combined (these are bit flags)
- NORMAL = 0x00,
- BOLD = 0x01,
- ITALIC = 0x02,
- UNDERLINE = 0x04
- };
-
- enum ShadowType
- {
- NO_SHADOW,
- DROP_SHADOW,
- DROP_SHADOW_SOFT
- };
-
- LLFontGL();
- ~LLFontGL();
-
-
- void reset(); // Reset a font after GL cleanup. ONLY works on an already loaded font.
-
- void destroyGL();
-
- bool loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n);
-
- S32 getNumFaces(const std::string& filename);
-
- 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,
- bool use_color = true) const;
-
- S32 render(const LLWString &text, S32 begin_offset,
- const LLRectf& 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,
- bool use_color = true) 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, ShadowType shadow = NO_SHADOW,
- S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX,
- F32* right_x=NULL,
- bool use_ellipses = false,
- bool use_color = true) const;
-
- S32 render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const;
-
- // renderUTF8 does a conversion, so is slower!
- 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, S32 max_pixels = S32_MAX, F32* right_x = NULL, bool use_ellipses = false, bool use_color = true) 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;
-
- // font metrics - override for LLFontFreetype that returns units of virtual pixels
- F32 getAscenderHeight() const;
- F32 getDescenderHeight() const;
- S32 getLineHeight() 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;
-
- 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, bool no_padding = false) 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
- 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
- // 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)
- 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;
-
- void generateASCIIglyphs();
-
-
- static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures = true);
-
- void dumpTextures();
- static void dumpFonts();
- static void dumpFontTextures();
-
- // Load sans-serif, sans-serif-small, etc.
- // Slow, requires multiple seconds to load fonts.
- static bool loadDefaultFonts();
- static void loadCommonFonts();
- static void destroyDefaultFonts();
- static void destroyAllGL();
-
- // 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 std::string nameFromFont(const LLFontGL* fontp);
- static std::string sizeFromFont(const LLFontGL* fontp);
-
- static std::string nameFromHAlign(LLFontGL::HAlign align);
- static LLFontGL::HAlign hAlignFromName(const std::string& name);
-
- static std::string nameFromVAlign(LLFontGL::VAlign align);
- static LLFontGL::VAlign vAlignFromName(const std::string& name);
-
- static void setFontDisplay(bool flag) { sDisplayFont = flag; }
-
- static LLFontGL* getFontEmojiSmall();
- static LLFontGL* getFontEmojiMedium();
- static LLFontGL* getFontEmojiLarge();
- static LLFontGL* getFontEmojiHuge();
- static LLFontGL* getFontMonospace();
- static LLFontGL* getFontSansSerifSmall();
- static LLFontGL* getFontSansSerifSmallBold();
- static LLFontGL* getFontSansSerifSmallItalic();
- static LLFontGL* getFontSansSerif();
- static LLFontGL* getFontSansSerifBig();
- static LLFontGL* getFontSansSerifHuge();
- static LLFontGL* getFontSansSerifBold();
- 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 LLCoordGL sCurOrigin;
- static F32 sCurDepth;
- static std::vector<std::pair<LLCoordGL, F32> > 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;
-
- LLFontGL(const LLFontGL &source);
- LLFontGL &operator=(const LLFontGL &source);
-
- LLFontDescriptor mFontDescriptor;
- LLPointer<LLFontFreetype> mFontFreetype;
-
- 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
+/**
+ * @file llfontgl.h
+ * @author Doug Soo
+ * @brief Wrapper around FreeType
+ *
+ * $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_LLFONTGL_H
+#define LL_LLFONTGL_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;
+
+// Structure used to store previously requested fonts.
+class LLFontRegistry;
+
+class LLFontGL
+{
+public:
+ enum HAlign
+ {
+ // Horizontal location of x,y coord to render.
+ LEFT = 0, // Left align
+ RIGHT = 1, // Right align
+ HCENTER = 2, // Center
+ };
+
+ enum VAlign
+ {
+ // Vertical location of x,y coord to render.
+ TOP = 3, // Top align
+ VCENTER = 4, // Center
+ BASELINE = 5, // Baseline
+ BOTTOM = 6 // Bottom
+ };
+
+ enum StyleFlags
+ {
+ // text style to render. May be combined (these are bit flags)
+ NORMAL = 0x00,
+ BOLD = 0x01,
+ ITALIC = 0x02,
+ UNDERLINE = 0x04
+ };
+
+ enum ShadowType
+ {
+ NO_SHADOW,
+ DROP_SHADOW,
+ DROP_SHADOW_SOFT
+ };
+
+ LLFontGL();
+ ~LLFontGL();
+
+
+ void reset(); // Reset a font after GL cleanup. ONLY works on an already loaded font.
+
+ void destroyGL();
+
+ bool loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n);
+
+ S32 getNumFaces(const std::string& filename);
+
+ 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,
+ bool use_color = true) const;
+
+ S32 render(const LLWString &text, S32 begin_offset,
+ const LLRectf& 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,
+ bool use_color = true) 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, ShadowType shadow = NO_SHADOW,
+ S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX,
+ F32* right_x=NULL,
+ bool use_ellipses = false,
+ bool use_color = true) const;
+
+ S32 render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const;
+
+ // renderUTF8 does a conversion, so is slower!
+ 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, S32 max_pixels = S32_MAX, F32* right_x = NULL, bool use_ellipses = false, bool use_color = true) 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;
+
+ // font metrics - override for LLFontFreetype that returns units of virtual pixels
+ F32 getAscenderHeight() const;
+ F32 getDescenderHeight() const;
+ S32 getLineHeight() 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;
+
+ 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, bool no_padding = false) 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
+ 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
+ // 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)
+ 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;
+
+ void generateASCIIglyphs();
+
+
+ static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures = true);
+
+ void dumpTextures();
+ static void dumpFonts();
+ static void dumpFontTextures();
+
+ // Load sans-serif, sans-serif-small, etc.
+ // Slow, requires multiple seconds to load fonts.
+ static bool loadDefaultFonts();
+ static void loadCommonFonts();
+ static void destroyDefaultFonts();
+ static void destroyAllGL();
+
+ // 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 std::string nameFromFont(const LLFontGL* fontp);
+ static std::string sizeFromFont(const LLFontGL* fontp);
+
+ static std::string nameFromHAlign(LLFontGL::HAlign align);
+ static LLFontGL::HAlign hAlignFromName(const std::string& name);
+
+ static std::string nameFromVAlign(LLFontGL::VAlign align);
+ static LLFontGL::VAlign vAlignFromName(const std::string& name);
+
+ static void setFontDisplay(bool flag) { sDisplayFont = flag; }
+
+ static LLFontGL* getFontEmojiSmall();
+ static LLFontGL* getFontEmojiMedium();
+ static LLFontGL* getFontEmojiLarge();
+ static LLFontGL* getFontEmojiHuge();
+ static LLFontGL* getFontMonospace();
+ static LLFontGL* getFontSansSerifSmall();
+ static LLFontGL* getFontSansSerifSmallBold();
+ static LLFontGL* getFontSansSerifSmallItalic();
+ static LLFontGL* getFontSansSerif();
+ static LLFontGL* getFontSansSerifBig();
+ static LLFontGL* getFontSansSerifHuge();
+ static LLFontGL* getFontSansSerifBold();
+ 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 LLCoordGL sCurOrigin;
+ static F32 sCurDepth;
+ static std::vector<std::pair<LLCoordGL, F32> > 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;
+
+ LLFontGL(const LLFontGL &source);
+ LLFontGL &operator=(const LLFontGL &source);
+
+ LLFontDescriptor mFontDescriptor;
+ LLPointer<LLFontFreetype> mFontFreetype;
+
+ 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
index 96a1c08b36..62f4f35e3d 100644
--- a/indra/llrender/llfontregistry.cpp
+++ b/indra/llrender/llfontregistry.cpp
@@ -1,762 +1,762 @@
-/**
- * @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"
-#include "llxmlnode.h"
-
-extern LLControlGroup gSavedSettings;
-
-using std::string;
-using std::map;
-
-bool font_desc_init_from_xml(LLXMLNodePtr node, LLFontDescriptor& desc);
-bool init_from_xml(LLFontRegistry* registry, LLXMLNodePtr node);
-
-const std::string MACOSX_FONT_PATH_LIBRARY = "/Library/Fonts/";
-const std::string MACOSX_FONT_SUPPLEMENTAL = "Supplemental/";
-
-LLFontDescriptor::char_functor_map_t LLFontDescriptor::mCharFunctors({
- { "is_emoji", LLStringOps::isEmoji }
-});
-
-LLFontDescriptor::LLFontDescriptor():
- mStyle(0)
-{
-}
-
-LLFontDescriptor::LLFontDescriptor(const std::string& name,
- const std::string& size,
- const U8 style,
- const font_file_info_vec_t& font_files):
- mName(name),
- mSize(size),
- mStyle(style),
- mFontFiles(font_files)
-{
-}
-
-LLFontDescriptor::LLFontDescriptor(const std::string& name,
- const std::string& size,
- const U8 style,
- const font_file_info_vec_t& font_list,
- const font_file_info_vec_t& font_collection_files) :
- LLFontDescriptor(name, size, style, font_list)
-{
- mFontCollectionFiles = font_collection_files;
-}
-
-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, getFontFiles(), getFontCollectionFiles());
-}
-
-void LLFontDescriptor::addFontFile(const std::string& file_name, const std::string& char_functor)
-{
- char_functor_map_t::const_iterator it = mCharFunctors.find(char_functor);
- mFontFiles.push_back(LLFontFileInfo(file_name, (mCharFunctors.end() != it) ? it->second : nullptr));
-}
-
-void LLFontDescriptor::addFontCollectionFile(const std::string& file_name, const std::string& char_functor)
-{
- char_functor_map_t::const_iterator it = mCharFunctors.find(char_functor);
- mFontCollectionFiles.push_back(LLFontFileInfo(file_name, (mCharFunctors.end() != it) ? it->second : nullptr));
-}
-
-LLFontRegistry::LLFontRegistry(bool create_gl_textures)
-: mCreateGLTextures(create_gl_textures)
-{
- // 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 and read at least one XUI file
- const string_vec_t xml_paths = gDirUtilp->findSkinnedFilenames(LLDir::XUI, xml_filename);
- if (xml_paths.empty())
- {
- // We didn't even find one single XUI file
- return false;
- }
-
- for (string_vec_t::const_iterator path_it = xml_paths.begin();
- path_it != xml_paths.end();
- ++path_it)
- {
- LLXMLNodePtr root;
- bool parsed_file = LLXMLNode::parseFile(*path_it, root, NULL);
-
- if (!parsed_file)
- continue;
-
- if ( root.isNull() || ! root->hasName( "fonts" ) )
- {
- LL_WARNS() << "Bad font info file: " << *path_it << LL_ENDL;
- 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 = init_from_xml(this, 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_LINUX
- return "Linux";
-#else
- return "";
-#endif
-}
-
-bool font_desc_init_from_xml(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();
- std::string char_functor;
-
- if (child->hasAttribute("functor"))
- {
- child->getAttributeString("functor", char_functor);
- }
-
- if (child->hasAttribute("load_collection"))
- {
- bool col = false;
- child->getAttributeBOOL("load_collection", col);
- if (col)
- {
- desc.addFontCollectionFile(font_file_name, char_functor);
- }
- }
-
- desc.addFontFile(font_file_name, char_functor);
- }
- else if (child->hasName("os"))
- {
- if (child_name == currentOsName())
- {
- font_desc_init_from_xml(child, desc);
- }
- }
- }
- return true;
-}
-
-bool init_from_xml(LLFontRegistry* registry, 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 = font_desc_init_from_xml(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 = registry->getMatchingFontDesc(desc);
- if (match_desc == NULL)
- {
- // Create a new entry (with no corresponding font).
- registry->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.
- font_file_info_vec_t font_files = match_desc->getFontFiles();
- font_files.insert(font_files.begin(),
- desc.getFontFiles().begin(),
- desc.getFontFiles().end());
-
- font_file_info_vec_t font_collection_files = match_desc->getFontCollectionFiles();
- font_collection_files.insert(font_collection_files.begin(),
- desc.getFontCollectionFiles().begin(),
- desc.getFontCollectionFiles().end());
-
- LLFontDescriptor new_desc = *match_desc;
- new_desc.setFontFiles(font_files);
- new_desc.setFontCollectionFiles(font_collection_files);
- registry->mFontMap.erase(*match_desc);
- registry->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))
- {
- registry->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)
- {
- LL_WARNS() << "createFont unrecognized size " << norm_desc.getSize() << LL_ENDL;
- return NULL;
- }
- LL_INFOS() << "createFont " << norm_desc.getName() << " size " << norm_desc.getSize() << " style " << ((S32) norm_desc.getStyle()) << LL_ENDL;
- 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)
- {
- LL_WARNS() << "createFont failed, no template found for "
- << norm_desc.getName() << " style [" << ((S32)norm_desc.getStyle()) << "]" << LL_ENDL;
- 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)
- {
- LL_INFOS() << "-- matching font exists: " << nearest_exact_desc.getName() << " size " << nearest_exact_desc.getSize() << " style " << ((S32) nearest_exact_desc.getStyle()) << LL_ENDL;
-
- // 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.
- font_file_info_vec_t font_files = match_desc->getFontFiles();
- font_file_info_vec_t font_collection_files = match_desc->getFontCollectionFiles();
- LLFontDescriptor default_desc("default",s_template_string,0);
- const LLFontDescriptor *match_default_desc = getMatchingFontDesc(default_desc);
- if (match_default_desc)
- {
- font_files.insert(font_files.end(),
- match_default_desc->getFontFiles().begin(),
- match_default_desc->getFontFiles().end());
- font_collection_files.insert(font_collection_files.end(),
- match_default_desc->getFontCollectionFiles().begin(),
- match_default_desc->getFontCollectionFiles().end());
- }
-
- // Add ultimate fallback list - generated dynamically on linux,
- // null elsewhere.
- std::transform(getUltimateFallbackList().begin(), getUltimateFallbackList().end(), std::back_inserter(font_files),
- [](const std::string& file_name) { return LLFontFileInfo(file_name); });
-
- // Load fonts based on names.
- if (font_files.empty())
- {
- LL_WARNS() << "createFont failed, no file names specified" << LL_ENDL;
- return NULL;
- }
-
- LLFontGL *result = NULL;
-
- // The first font will get pulled will be the "head" font, set to non-fallback.
- // Rest will consitute the fallback list.
- bool is_first_found = true;
-
- string_vec_t font_search_paths;
- font_search_paths.push_back(LLFontGL::getFontPathLocal());
- font_search_paths.push_back(LLFontGL::getFontPathSystem());
-#if LL_DARWIN
- font_search_paths.push_back(MACOSX_FONT_PATH_LIBRARY);
- font_search_paths.push_back(MACOSX_FONT_PATH_LIBRARY + MACOSX_FONT_SUPPLEMENTAL);
- font_search_paths.push_back(LLFontGL::getFontPathSystem() + MACOSX_FONT_SUPPLEMENTAL);
-#endif
-
- // The fontname string may contain multiple font file names separated by semicolons.
- // Break it apart and try loading each one, in order.
- for(font_file_info_vec_t::iterator font_file_it = font_files.begin();
- font_file_it != font_files.end();
- ++font_file_it)
- {
- LLFontGL *fontp = NULL;
-
- bool is_ft_collection = (std::find_if(font_collection_files.begin(), font_collection_files.end(),
- [&font_file_it](const LLFontFileInfo& ffi) { return font_file_it->FileName == ffi.FileName; }) != font_collection_files.end());
-
- // *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;
- F32 point_size_scale = extra_scale * point_size;
- bool is_font_loaded = false;
- for(string_vec_t::iterator font_search_path_it = font_search_paths.begin();
- font_search_path_it != font_search_paths.end();
- ++font_search_path_it)
- {
- const std::string font_path = *font_search_path_it + font_file_it->FileName;
-
- fontp = new LLFontGL;
- S32 num_faces = is_ft_collection ? fontp->getNumFaces(font_path) : 1;
- for (S32 i = 0; i < num_faces; i++)
- {
- if (fontp == NULL)
- {
- fontp = new LLFontGL;
- }
- if (fontp->loadFace(font_path, point_size_scale,
- LLFontGL::sVertDPI, LLFontGL::sHorizDPI, is_fallback, i))
- {
- is_font_loaded = true;
- if (is_first_found)
- {
- result = fontp;
- is_first_found = false;
- }
- else
- {
- result->mFontFreetype->addFallbackFont(fontp->mFontFreetype, font_file_it->CharFunctor);
-
- delete fontp;
- fontp = NULL;
- }
- }
- else
- {
- delete fontp;
- fontp = NULL;
- }
- }
- if (is_font_loaded) break;
- }
- if(!is_font_loaded)
- {
- LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << font_file_it->FileName << LL_ENDL;
- delete fontp;
- fontp = NULL;
- }
- }
-
- if (result)
- {
- result->mFontDescriptor = desc;
- }
- else
- {
- LL_WARNS() << "createFont failed in some way" << LL_ENDL;
- }
-
- 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)
- {
- LL_WARNS() << "getFont failed, name " << desc.getName()
- <<" style=[" << ((S32) desc.getStyle()) << "]"
- << " size=[" << desc.getSize() << "]" << LL_ENDL;
- }
- else
- {
- //generate glyphs for ASCII chars to avoid stalls later
- fontp->generateASCIIglyphs();
- }
- 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()
-{
- LL_INFOS() << "LLFontRegistry dump: " << LL_ENDL;
- for (font_size_map_t::iterator size_it = mFontSizes.begin();
- size_it != mFontSizes.end();
- ++size_it)
- {
- LL_INFOS() << "Size: " << size_it->first << " => " << size_it->second << LL_ENDL;
- }
- for (font_reg_map_t::iterator font_it = mFontMap.begin();
- font_it != mFontMap.end();
- ++font_it)
- {
- const LLFontDescriptor& desc = font_it->first;
- LL_INFOS() << "Font: name=" << desc.getName()
- << " style=[" << ((S32)desc.getStyle()) << "]"
- << " size=[" << desc.getSize() << "]"
- << " fileNames="
- << LL_ENDL;
- for (font_file_info_vec_t::const_iterator file_it=desc.getFontFiles().begin();
- file_it != desc.getFontFiles().end();
- ++file_it)
- {
- LL_INFOS() << " file: " << file_it->FileName << LL_ENDL;
- }
- }
-}
-
-void LLFontRegistry::dumpTextures()
-{
- for (const auto& fontEntry : mFontMap)
- {
- if (fontEntry.second)
- {
- fontEntry.second->dumpTextures();
- }
- }
-}
-
-const string_vec_t& LLFontRegistry::getUltimateFallbackList() const
-{
- return mUltimateFallbackList;
-}
+/**
+ * @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"
+#include "llxmlnode.h"
+
+extern LLControlGroup gSavedSettings;
+
+using std::string;
+using std::map;
+
+bool font_desc_init_from_xml(LLXMLNodePtr node, LLFontDescriptor& desc);
+bool init_from_xml(LLFontRegistry* registry, LLXMLNodePtr node);
+
+const std::string MACOSX_FONT_PATH_LIBRARY = "/Library/Fonts/";
+const std::string MACOSX_FONT_SUPPLEMENTAL = "Supplemental/";
+
+LLFontDescriptor::char_functor_map_t LLFontDescriptor::mCharFunctors({
+ { "is_emoji", LLStringOps::isEmoji }
+});
+
+LLFontDescriptor::LLFontDescriptor():
+ mStyle(0)
+{
+}
+
+LLFontDescriptor::LLFontDescriptor(const std::string& name,
+ const std::string& size,
+ const U8 style,
+ const font_file_info_vec_t& font_files):
+ mName(name),
+ mSize(size),
+ mStyle(style),
+ mFontFiles(font_files)
+{
+}
+
+LLFontDescriptor::LLFontDescriptor(const std::string& name,
+ const std::string& size,
+ const U8 style,
+ const font_file_info_vec_t& font_list,
+ const font_file_info_vec_t& font_collection_files) :
+ LLFontDescriptor(name, size, style, font_list)
+{
+ mFontCollectionFiles = font_collection_files;
+}
+
+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, getFontFiles(), getFontCollectionFiles());
+}
+
+void LLFontDescriptor::addFontFile(const std::string& file_name, const std::string& char_functor)
+{
+ char_functor_map_t::const_iterator it = mCharFunctors.find(char_functor);
+ mFontFiles.push_back(LLFontFileInfo(file_name, (mCharFunctors.end() != it) ? it->second : nullptr));
+}
+
+void LLFontDescriptor::addFontCollectionFile(const std::string& file_name, const std::string& char_functor)
+{
+ char_functor_map_t::const_iterator it = mCharFunctors.find(char_functor);
+ mFontCollectionFiles.push_back(LLFontFileInfo(file_name, (mCharFunctors.end() != it) ? it->second : nullptr));
+}
+
+LLFontRegistry::LLFontRegistry(bool create_gl_textures)
+: mCreateGLTextures(create_gl_textures)
+{
+ // 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 and read at least one XUI file
+ const string_vec_t xml_paths = gDirUtilp->findSkinnedFilenames(LLDir::XUI, xml_filename);
+ if (xml_paths.empty())
+ {
+ // We didn't even find one single XUI file
+ return false;
+ }
+
+ for (string_vec_t::const_iterator path_it = xml_paths.begin();
+ path_it != xml_paths.end();
+ ++path_it)
+ {
+ LLXMLNodePtr root;
+ bool parsed_file = LLXMLNode::parseFile(*path_it, root, NULL);
+
+ if (!parsed_file)
+ continue;
+
+ if ( root.isNull() || ! root->hasName( "fonts" ) )
+ {
+ LL_WARNS() << "Bad font info file: " << *path_it << LL_ENDL;
+ 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 = init_from_xml(this, 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_LINUX
+ return "Linux";
+#else
+ return "";
+#endif
+}
+
+bool font_desc_init_from_xml(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();
+ std::string char_functor;
+
+ if (child->hasAttribute("functor"))
+ {
+ child->getAttributeString("functor", char_functor);
+ }
+
+ if (child->hasAttribute("load_collection"))
+ {
+ bool col = false;
+ child->getAttributeBOOL("load_collection", col);
+ if (col)
+ {
+ desc.addFontCollectionFile(font_file_name, char_functor);
+ }
+ }
+
+ desc.addFontFile(font_file_name, char_functor);
+ }
+ else if (child->hasName("os"))
+ {
+ if (child_name == currentOsName())
+ {
+ font_desc_init_from_xml(child, desc);
+ }
+ }
+ }
+ return true;
+}
+
+bool init_from_xml(LLFontRegistry* registry, 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 = font_desc_init_from_xml(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 = registry->getMatchingFontDesc(desc);
+ if (match_desc == NULL)
+ {
+ // Create a new entry (with no corresponding font).
+ registry->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.
+ font_file_info_vec_t font_files = match_desc->getFontFiles();
+ font_files.insert(font_files.begin(),
+ desc.getFontFiles().begin(),
+ desc.getFontFiles().end());
+
+ font_file_info_vec_t font_collection_files = match_desc->getFontCollectionFiles();
+ font_collection_files.insert(font_collection_files.begin(),
+ desc.getFontCollectionFiles().begin(),
+ desc.getFontCollectionFiles().end());
+
+ LLFontDescriptor new_desc = *match_desc;
+ new_desc.setFontFiles(font_files);
+ new_desc.setFontCollectionFiles(font_collection_files);
+ registry->mFontMap.erase(*match_desc);
+ registry->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))
+ {
+ registry->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)
+ {
+ LL_WARNS() << "createFont unrecognized size " << norm_desc.getSize() << LL_ENDL;
+ return NULL;
+ }
+ LL_INFOS() << "createFont " << norm_desc.getName() << " size " << norm_desc.getSize() << " style " << ((S32) norm_desc.getStyle()) << LL_ENDL;
+ 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)
+ {
+ LL_WARNS() << "createFont failed, no template found for "
+ << norm_desc.getName() << " style [" << ((S32)norm_desc.getStyle()) << "]" << LL_ENDL;
+ 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)
+ {
+ LL_INFOS() << "-- matching font exists: " << nearest_exact_desc.getName() << " size " << nearest_exact_desc.getSize() << " style " << ((S32) nearest_exact_desc.getStyle()) << LL_ENDL;
+
+ // 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.
+ font_file_info_vec_t font_files = match_desc->getFontFiles();
+ font_file_info_vec_t font_collection_files = match_desc->getFontCollectionFiles();
+ LLFontDescriptor default_desc("default",s_template_string,0);
+ const LLFontDescriptor *match_default_desc = getMatchingFontDesc(default_desc);
+ if (match_default_desc)
+ {
+ font_files.insert(font_files.end(),
+ match_default_desc->getFontFiles().begin(),
+ match_default_desc->getFontFiles().end());
+ font_collection_files.insert(font_collection_files.end(),
+ match_default_desc->getFontCollectionFiles().begin(),
+ match_default_desc->getFontCollectionFiles().end());
+ }
+
+ // Add ultimate fallback list - generated dynamically on linux,
+ // null elsewhere.
+ std::transform(getUltimateFallbackList().begin(), getUltimateFallbackList().end(), std::back_inserter(font_files),
+ [](const std::string& file_name) { return LLFontFileInfo(file_name); });
+
+ // Load fonts based on names.
+ if (font_files.empty())
+ {
+ LL_WARNS() << "createFont failed, no file names specified" << LL_ENDL;
+ return NULL;
+ }
+
+ LLFontGL *result = NULL;
+
+ // The first font will get pulled will be the "head" font, set to non-fallback.
+ // Rest will consitute the fallback list.
+ bool is_first_found = true;
+
+ string_vec_t font_search_paths;
+ font_search_paths.push_back(LLFontGL::getFontPathLocal());
+ font_search_paths.push_back(LLFontGL::getFontPathSystem());
+#if LL_DARWIN
+ font_search_paths.push_back(MACOSX_FONT_PATH_LIBRARY);
+ font_search_paths.push_back(MACOSX_FONT_PATH_LIBRARY + MACOSX_FONT_SUPPLEMENTAL);
+ font_search_paths.push_back(LLFontGL::getFontPathSystem() + MACOSX_FONT_SUPPLEMENTAL);
+#endif
+
+ // The fontname string may contain multiple font file names separated by semicolons.
+ // Break it apart and try loading each one, in order.
+ for(font_file_info_vec_t::iterator font_file_it = font_files.begin();
+ font_file_it != font_files.end();
+ ++font_file_it)
+ {
+ LLFontGL *fontp = NULL;
+
+ bool is_ft_collection = (std::find_if(font_collection_files.begin(), font_collection_files.end(),
+ [&font_file_it](const LLFontFileInfo& ffi) { return font_file_it->FileName == ffi.FileName; }) != font_collection_files.end());
+
+ // *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;
+ F32 point_size_scale = extra_scale * point_size;
+ bool is_font_loaded = false;
+ for(string_vec_t::iterator font_search_path_it = font_search_paths.begin();
+ font_search_path_it != font_search_paths.end();
+ ++font_search_path_it)
+ {
+ const std::string font_path = *font_search_path_it + font_file_it->FileName;
+
+ fontp = new LLFontGL;
+ S32 num_faces = is_ft_collection ? fontp->getNumFaces(font_path) : 1;
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ if (fontp == NULL)
+ {
+ fontp = new LLFontGL;
+ }
+ if (fontp->loadFace(font_path, point_size_scale,
+ LLFontGL::sVertDPI, LLFontGL::sHorizDPI, is_fallback, i))
+ {
+ is_font_loaded = true;
+ if (is_first_found)
+ {
+ result = fontp;
+ is_first_found = false;
+ }
+ else
+ {
+ result->mFontFreetype->addFallbackFont(fontp->mFontFreetype, font_file_it->CharFunctor);
+
+ delete fontp;
+ fontp = NULL;
+ }
+ }
+ else
+ {
+ delete fontp;
+ fontp = NULL;
+ }
+ }
+ if (is_font_loaded) break;
+ }
+ if(!is_font_loaded)
+ {
+ LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << font_file_it->FileName << LL_ENDL;
+ delete fontp;
+ fontp = NULL;
+ }
+ }
+
+ if (result)
+ {
+ result->mFontDescriptor = desc;
+ }
+ else
+ {
+ LL_WARNS() << "createFont failed in some way" << LL_ENDL;
+ }
+
+ 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)
+ {
+ LL_WARNS() << "getFont failed, name " << desc.getName()
+ <<" style=[" << ((S32) desc.getStyle()) << "]"
+ << " size=[" << desc.getSize() << "]" << LL_ENDL;
+ }
+ else
+ {
+ //generate glyphs for ASCII chars to avoid stalls later
+ fontp->generateASCIIglyphs();
+ }
+ 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()
+{
+ LL_INFOS() << "LLFontRegistry dump: " << LL_ENDL;
+ for (font_size_map_t::iterator size_it = mFontSizes.begin();
+ size_it != mFontSizes.end();
+ ++size_it)
+ {
+ LL_INFOS() << "Size: " << size_it->first << " => " << size_it->second << LL_ENDL;
+ }
+ for (font_reg_map_t::iterator font_it = mFontMap.begin();
+ font_it != mFontMap.end();
+ ++font_it)
+ {
+ const LLFontDescriptor& desc = font_it->first;
+ LL_INFOS() << "Font: name=" << desc.getName()
+ << " style=[" << ((S32)desc.getStyle()) << "]"
+ << " size=[" << desc.getSize() << "]"
+ << " fileNames="
+ << LL_ENDL;
+ for (font_file_info_vec_t::const_iterator file_it=desc.getFontFiles().begin();
+ file_it != desc.getFontFiles().end();
+ ++file_it)
+ {
+ LL_INFOS() << " file: " << file_it->FileName << LL_ENDL;
+ }
+ }
+}
+
+void LLFontRegistry::dumpTextures()
+{
+ for (const auto& fontEntry : mFontMap)
+ {
+ if (fontEntry.second)
+ {
+ fontEntry.second->dumpTextures();
+ }
+ }
+}
+
+const string_vec_t& LLFontRegistry::getUltimateFallbackList() const
+{
+ return mUltimateFallbackList;
+}
diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp
index 0508acbd54..ac697b72be 100644
--- a/indra/llrender/llgl.cpp
+++ b/indra/llrender/llgl.cpp
@@ -1,2944 +1,2944 @@
-/**
- * @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 "llglstates.h"
-#include "llrender.h"
-
-#include "llerror.h"
-#include "llerrorcontrol.h"
-#include "llquaternion.h"
-#include "llmath.h"
-#include "m4math.h"
-#include "llstring.h"
-#include "llstacktrace.h"
-
-#include "llglheaders.h"
-#include "llglslshader.h"
-
-#if LL_WINDOWS
-#include "lldxhardware.h"
-#endif
-
-#ifdef _DEBUG
-//#define GL_STATE_VERIFY
-#endif
-
-
-bool gDebugSession = false;
-bool gDebugGLSession = false;
-bool gClothRipple = false;
-bool gHeadlessClient = false;
-bool gNonInteractive = false;
-bool gGLActive = false;
-
-static const std::string HEADLESS_VENDOR_STRING("Linden Lab");
-static const std::string HEADLESS_RENDERER_STRING("Headless");
-static const std::string HEADLESS_VERSION_STRING("1.0");
-
-llofstream gFailLog;
-
-#if GL_ARB_debug_output
-
-#ifndef APIENTRY
-#define APIENTRY
-#endif
-
-void APIENTRY gl_debug_callback(GLenum source,
- GLenum type,
- GLuint id,
- GLenum severity,
- GLsizei length,
- const GLchar* message,
- GLvoid* userParam)
-{
- /*if (severity != GL_DEBUG_SEVERITY_HIGH &&
- severity != GL_DEBUG_SEVERITY_MEDIUM &&
- severity != GL_DEBUG_SEVERITY_LOW
- )
- { //suppress out-of-spec messages sent by nvidia driver (mostly vertexbuffer hints)
- return;
- }*/
-
- if (gGLManager.mIsDisabled &&
- severity == GL_DEBUG_SEVERITY_HIGH_ARB &&
- source == GL_DEBUG_SOURCE_API_ARB &&
- type == GL_DEBUG_TYPE_ERROR_ARB &&
- id == GL_INVALID_VALUE)
- {
- // Suppress messages about deleting already deleted objects called from LLViewerWindow::stopGL()
- // "GL_INVALID_VALUE error generated. Handle does not refer to an object generated by OpenGL."
- return;
- }
-
- // list of messages to suppress
- const char* suppress[] =
- {
- "Buffer detailed info:",
- "Program undefined behavior warning: The current GL state uses a sampler (0) that has depth comparisons enabled"
- };
-
- for (const char* msg : suppress)
- {
- if (strncmp(msg, message, strlen(msg)) == 0)
- {
- return;
- }
- }
-
- if (severity == GL_DEBUG_SEVERITY_HIGH)
- {
- LL_WARNS() << "----- GL ERROR --------" << LL_ENDL;
- }
- else
- {
- LL_WARNS() << "----- GL WARNING -------" << LL_ENDL;
- }
- LL_WARNS() << "Type: " << std::hex << type << LL_ENDL;
- LL_WARNS() << "ID: " << std::hex << id << LL_ENDL;
- LL_WARNS() << "Severity: " << std::hex << severity << LL_ENDL;
- LL_WARNS() << "Message: " << message << LL_ENDL;
- LL_WARNS() << "-----------------------" << LL_ENDL;
-
- GLint vao = 0;
- glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vao);
- GLint vbo = 0;
- glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo);
- GLint vbo_size = 0;
- if (vbo != 0)
- {
- glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &vbo_size);
- }
- GLint ibo = 0;
- glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &ibo);
- GLint ibo_size = 0;
- if (ibo != 0)
- {
- glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &ibo_size);
- }
- GLint ubo = 0;
- glGetIntegerv(GL_UNIFORM_BUFFER_BINDING, &ubo);
- GLint ubo_size = 0;
- GLint ubo_immutable = 0;
- if (ubo != 0)
- {
- glGetBufferParameteriv(GL_UNIFORM_BUFFER, GL_BUFFER_SIZE, &ubo_size);
- glGetBufferParameteriv(GL_UNIFORM_BUFFER, GL_BUFFER_IMMUTABLE_STORAGE, &ubo_immutable);
- }
-
- // No needs to halt when is called from LLViewerWindow::stopGL()
- if (severity == GL_DEBUG_SEVERITY_HIGH && !gGLManager.mIsDisabled)
- {
- LL_ERRS() << "Halting on GL Error" << LL_ENDL;
- }
-}
-#endif
-
-void parse_glsl_version(S32& major, S32& minor);
-
-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
-
-std::list<LLGLUpdate*> LLGLUpdate::sGLQ;
-
-#if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS
-
-#if LL_WINDOWS
-// WGL_ARB_create_context
-PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr;
-
-// WGL_AMD_gpu_association
-PFNWGLGETGPUIDSAMDPROC wglGetGPUIDsAMD = nullptr;
-PFNWGLGETGPUINFOAMDPROC wglGetGPUInfoAMD = nullptr;
-PFNWGLGETCONTEXTGPUIDAMDPROC wglGetContextGPUIDAMD = nullptr;
-PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC wglCreateAssociatedContextAMD = nullptr;
-PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC wglCreateAssociatedContextAttribsAMD = nullptr;
-PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC wglDeleteAssociatedContextAMD = nullptr;
-PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC wglMakeAssociatedContextCurrentAMD = nullptr;
-PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC wglGetCurrentAssociatedContextAMD = nullptr;
-PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC wglBlitContextFramebufferAMD = nullptr;
-
-// WGL_EXT_swap_control
-PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr;
-PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = nullptr;
-
-#endif
-
-// GL_VERSION_1_2
-//PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements = nullptr;
-//PFNGLTEXIMAGE3DPROC glTexImage3D = nullptr;
-//PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D = nullptr;
-//PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D = nullptr;
-
-// GL_VERSION_1_3
-PFNGLACTIVETEXTUREPROC glActiveTexture = nullptr;
-PFNGLSAMPLECOVERAGEPROC glSampleCoverage = nullptr;
-PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D = nullptr;
-PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D = nullptr;
-PFNGLCOMPRESSEDTEXIMAGE1DPROC glCompressedTexImage1D = nullptr;
-PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glCompressedTexSubImage3D = nullptr;
-PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2D = nullptr;
-PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glCompressedTexSubImage1D = nullptr;
-PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage = nullptr;
-PFNGLCLIENTACTIVETEXTUREPROC glClientActiveTexture = nullptr;
-PFNGLMULTITEXCOORD1DPROC glMultiTexCoord1d = nullptr;
-PFNGLMULTITEXCOORD1DVPROC glMultiTexCoord1dv = nullptr;
-PFNGLMULTITEXCOORD1FPROC glMultiTexCoord1f = nullptr;
-PFNGLMULTITEXCOORD1FVPROC glMultiTexCoord1fv = nullptr;
-PFNGLMULTITEXCOORD1IPROC glMultiTexCoord1i = nullptr;
-PFNGLMULTITEXCOORD1IVPROC glMultiTexCoord1iv = nullptr;
-PFNGLMULTITEXCOORD1SPROC glMultiTexCoord1s = nullptr;
-PFNGLMULTITEXCOORD1SVPROC glMultiTexCoord1sv = nullptr;
-PFNGLMULTITEXCOORD2DPROC glMultiTexCoord2d = nullptr;
-PFNGLMULTITEXCOORD2DVPROC glMultiTexCoord2dv = nullptr;
-PFNGLMULTITEXCOORD2FPROC glMultiTexCoord2f = nullptr;
-PFNGLMULTITEXCOORD2FVPROC glMultiTexCoord2fv = nullptr;
-PFNGLMULTITEXCOORD2IPROC glMultiTexCoord2i = nullptr;
-PFNGLMULTITEXCOORD2IVPROC glMultiTexCoord2iv = nullptr;
-PFNGLMULTITEXCOORD2SPROC glMultiTexCoord2s = nullptr;
-PFNGLMULTITEXCOORD2SVPROC glMultiTexCoord2sv = nullptr;
-PFNGLMULTITEXCOORD3DPROC glMultiTexCoord3d = nullptr;
-PFNGLMULTITEXCOORD3DVPROC glMultiTexCoord3dv = nullptr;
-PFNGLMULTITEXCOORD3FPROC glMultiTexCoord3f = nullptr;
-PFNGLMULTITEXCOORD3FVPROC glMultiTexCoord3fv = nullptr;
-PFNGLMULTITEXCOORD3IPROC glMultiTexCoord3i = nullptr;
-PFNGLMULTITEXCOORD3IVPROC glMultiTexCoord3iv = nullptr;
-PFNGLMULTITEXCOORD3SPROC glMultiTexCoord3s = nullptr;
-PFNGLMULTITEXCOORD3SVPROC glMultiTexCoord3sv = nullptr;
-PFNGLMULTITEXCOORD4DPROC glMultiTexCoord4d = nullptr;
-PFNGLMULTITEXCOORD4DVPROC glMultiTexCoord4dv = nullptr;
-PFNGLMULTITEXCOORD4FPROC glMultiTexCoord4f = nullptr;
-PFNGLMULTITEXCOORD4FVPROC glMultiTexCoord4fv = nullptr;
-PFNGLMULTITEXCOORD4IPROC glMultiTexCoord4i = nullptr;
-PFNGLMULTITEXCOORD4IVPROC glMultiTexCoord4iv = nullptr;
-PFNGLMULTITEXCOORD4SPROC glMultiTexCoord4s = nullptr;
-PFNGLMULTITEXCOORD4SVPROC glMultiTexCoord4sv = nullptr;
-PFNGLLOADTRANSPOSEMATRIXFPROC glLoadTransposeMatrixf = nullptr;
-PFNGLLOADTRANSPOSEMATRIXDPROC glLoadTransposeMatrixd = nullptr;
-PFNGLMULTTRANSPOSEMATRIXFPROC glMultTransposeMatrixf = nullptr;
-PFNGLMULTTRANSPOSEMATRIXDPROC glMultTransposeMatrixd = nullptr;
-
-// GL_VERSION_1_4
-PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate = nullptr;
-PFNGLMULTIDRAWARRAYSPROC glMultiDrawArrays = nullptr;
-PFNGLMULTIDRAWELEMENTSPROC glMultiDrawElements = nullptr;
-PFNGLPOINTPARAMETERFPROC glPointParameterf = nullptr;
-PFNGLPOINTPARAMETERFVPROC glPointParameterfv = nullptr;
-PFNGLPOINTPARAMETERIPROC glPointParameteri = nullptr;
-PFNGLPOINTPARAMETERIVPROC glPointParameteriv = nullptr;
-PFNGLFOGCOORDFPROC glFogCoordf = nullptr;
-PFNGLFOGCOORDFVPROC glFogCoordfv = nullptr;
-PFNGLFOGCOORDDPROC glFogCoordd = nullptr;
-PFNGLFOGCOORDDVPROC glFogCoorddv = nullptr;
-PFNGLFOGCOORDPOINTERPROC glFogCoordPointer = nullptr;
-PFNGLSECONDARYCOLOR3BPROC glSecondaryColor3b = nullptr;
-PFNGLSECONDARYCOLOR3BVPROC glSecondaryColor3bv = nullptr;
-PFNGLSECONDARYCOLOR3DPROC glSecondaryColor3d = nullptr;
-PFNGLSECONDARYCOLOR3DVPROC glSecondaryColor3dv = nullptr;
-PFNGLSECONDARYCOLOR3FPROC glSecondaryColor3f = nullptr;
-PFNGLSECONDARYCOLOR3FVPROC glSecondaryColor3fv = nullptr;
-PFNGLSECONDARYCOLOR3IPROC glSecondaryColor3i = nullptr;
-PFNGLSECONDARYCOLOR3IVPROC glSecondaryColor3iv = nullptr;
-PFNGLSECONDARYCOLOR3SPROC glSecondaryColor3s = nullptr;
-PFNGLSECONDARYCOLOR3SVPROC glSecondaryColor3sv = nullptr;
-PFNGLSECONDARYCOLOR3UBPROC glSecondaryColor3ub = nullptr;
-PFNGLSECONDARYCOLOR3UBVPROC glSecondaryColor3ubv = nullptr;
-PFNGLSECONDARYCOLOR3UIPROC glSecondaryColor3ui = nullptr;
-PFNGLSECONDARYCOLOR3UIVPROC glSecondaryColor3uiv = nullptr;
-PFNGLSECONDARYCOLOR3USPROC glSecondaryColor3us = nullptr;
-PFNGLSECONDARYCOLOR3USVPROC glSecondaryColor3usv = nullptr;
-PFNGLSECONDARYCOLORPOINTERPROC glSecondaryColorPointer = nullptr;
-PFNGLWINDOWPOS2DPROC glWindowPos2d = nullptr;
-PFNGLWINDOWPOS2DVPROC glWindowPos2dv = nullptr;
-PFNGLWINDOWPOS2FPROC glWindowPos2f = nullptr;
-PFNGLWINDOWPOS2FVPROC glWindowPos2fv = nullptr;
-PFNGLWINDOWPOS2IPROC glWindowPos2i = nullptr;
-PFNGLWINDOWPOS2IVPROC glWindowPos2iv = nullptr;
-PFNGLWINDOWPOS2SPROC glWindowPos2s = nullptr;
-PFNGLWINDOWPOS2SVPROC glWindowPos2sv = nullptr;
-PFNGLWINDOWPOS3DPROC glWindowPos3d = nullptr;
-PFNGLWINDOWPOS3DVPROC glWindowPos3dv = nullptr;
-PFNGLWINDOWPOS3FPROC glWindowPos3f = nullptr;
-PFNGLWINDOWPOS3FVPROC glWindowPos3fv = nullptr;
-PFNGLWINDOWPOS3IPROC glWindowPos3i = nullptr;
-PFNGLWINDOWPOS3IVPROC glWindowPos3iv = nullptr;
-PFNGLWINDOWPOS3SPROC glWindowPos3s = nullptr;
-PFNGLWINDOWPOS3SVPROC glWindowPos3sv = nullptr;
-
-// GL_VERSION_1_5
-PFNGLGENQUERIESPROC glGenQueries = nullptr;
-PFNGLDELETEQUERIESPROC glDeleteQueries = nullptr;
-PFNGLISQUERYPROC glIsQuery = nullptr;
-PFNGLBEGINQUERYPROC glBeginQuery = nullptr;
-PFNGLENDQUERYPROC glEndQuery = nullptr;
-PFNGLGETQUERYIVPROC glGetQueryiv = nullptr;
-PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv = nullptr;
-PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv = nullptr;
-PFNGLBINDBUFFERPROC glBindBuffer = nullptr;
-PFNGLDELETEBUFFERSPROC glDeleteBuffers = nullptr;
-PFNGLGENBUFFERSPROC glGenBuffers = nullptr;
-PFNGLISBUFFERPROC glIsBuffer = nullptr;
-PFNGLBUFFERDATAPROC glBufferData = nullptr;
-PFNGLBUFFERSUBDATAPROC glBufferSubData = nullptr;
-PFNGLGETBUFFERSUBDATAPROC glGetBufferSubData = nullptr;
-PFNGLMAPBUFFERPROC glMapBuffer = nullptr;
-PFNGLUNMAPBUFFERPROC glUnmapBuffer = nullptr;
-PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv = nullptr;
-PFNGLGETBUFFERPOINTERVPROC glGetBufferPointerv = nullptr;
-
-// GL_VERSION_2_0
-PFNGLBLENDEQUATIONSEPARATEPROC glBlendEquationSeparate = nullptr;
-PFNGLDRAWBUFFERSPROC glDrawBuffers = nullptr;
-PFNGLSTENCILOPSEPARATEPROC glStencilOpSeparate = nullptr;
-PFNGLSTENCILFUNCSEPARATEPROC glStencilFuncSeparate = nullptr;
-PFNGLSTENCILMASKSEPARATEPROC glStencilMaskSeparate = nullptr;
-PFNGLATTACHSHADERPROC glAttachShader = nullptr;
-PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation = nullptr;
-PFNGLCOMPILESHADERPROC glCompileShader = nullptr;
-PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr;
-PFNGLCREATESHADERPROC glCreateShader = nullptr;
-PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr;
-PFNGLDELETESHADERPROC glDeleteShader = nullptr;
-PFNGLDETACHSHADERPROC glDetachShader = nullptr;
-PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray = nullptr;
-PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr;
-PFNGLGETACTIVEATTRIBPROC glGetActiveAttrib = nullptr;
-PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform = nullptr;
-PFNGLGETATTACHEDSHADERSPROC glGetAttachedShaders = nullptr;
-PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation = nullptr;
-PFNGLGETPROGRAMIVPROC glGetProgramiv = nullptr;
-PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = nullptr;
-PFNGLGETSHADERIVPROC glGetShaderiv = nullptr;
-PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = nullptr;
-PFNGLGETSHADERSOURCEPROC glGetShaderSource = nullptr;
-PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr;
-PFNGLGETUNIFORMFVPROC glGetUniformfv = nullptr;
-PFNGLGETUNIFORMIVPROC glGetUniformiv = nullptr;
-PFNGLGETVERTEXATTRIBDVPROC glGetVertexAttribdv = nullptr;
-PFNGLGETVERTEXATTRIBFVPROC glGetVertexAttribfv = nullptr;
-PFNGLGETVERTEXATTRIBIVPROC glGetVertexAttribiv = nullptr;
-PFNGLGETVERTEXATTRIBPOINTERVPROC glGetVertexAttribPointerv = nullptr;
-PFNGLISPROGRAMPROC glIsProgram = nullptr;
-PFNGLISSHADERPROC glIsShader = nullptr;
-PFNGLLINKPROGRAMPROC glLinkProgram = nullptr;
-PFNGLSHADERSOURCEPROC glShaderSource = nullptr;
-PFNGLUSEPROGRAMPROC glUseProgram = nullptr;
-PFNGLUNIFORM1FPROC glUniform1f = nullptr;
-PFNGLUNIFORM2FPROC glUniform2f = nullptr;
-PFNGLUNIFORM3FPROC glUniform3f = nullptr;
-PFNGLUNIFORM4FPROC glUniform4f = nullptr;
-PFNGLUNIFORM1IPROC glUniform1i = nullptr;
-PFNGLUNIFORM2IPROC glUniform2i = nullptr;
-PFNGLUNIFORM3IPROC glUniform3i = nullptr;
-PFNGLUNIFORM4IPROC glUniform4i = nullptr;
-PFNGLUNIFORM1FVPROC glUniform1fv = nullptr;
-PFNGLUNIFORM2FVPROC glUniform2fv = nullptr;
-PFNGLUNIFORM3FVPROC glUniform3fv = nullptr;
-PFNGLUNIFORM4FVPROC glUniform4fv = nullptr;
-PFNGLUNIFORM1IVPROC glUniform1iv = nullptr;
-PFNGLUNIFORM2IVPROC glUniform2iv = nullptr;
-PFNGLUNIFORM3IVPROC glUniform3iv = nullptr;
-PFNGLUNIFORM4IVPROC glUniform4iv = nullptr;
-PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv = nullptr;
-PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv = nullptr;
-PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = nullptr;
-PFNGLVALIDATEPROGRAMPROC glValidateProgram = nullptr;
-PFNGLVERTEXATTRIB1DPROC glVertexAttrib1d = nullptr;
-PFNGLVERTEXATTRIB1DVPROC glVertexAttrib1dv = nullptr;
-PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f = nullptr;
-PFNGLVERTEXATTRIB1FVPROC glVertexAttrib1fv = nullptr;
-PFNGLVERTEXATTRIB1SPROC glVertexAttrib1s = nullptr;
-PFNGLVERTEXATTRIB1SVPROC glVertexAttrib1sv = nullptr;
-PFNGLVERTEXATTRIB2DPROC glVertexAttrib2d = nullptr;
-PFNGLVERTEXATTRIB2DVPROC glVertexAttrib2dv = nullptr;
-PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f = nullptr;
-PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv = nullptr;
-PFNGLVERTEXATTRIB2SPROC glVertexAttrib2s = nullptr;
-PFNGLVERTEXATTRIB2SVPROC glVertexAttrib2sv = nullptr;
-PFNGLVERTEXATTRIB3DPROC glVertexAttrib3d = nullptr;
-PFNGLVERTEXATTRIB3DVPROC glVertexAttrib3dv = nullptr;
-PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f = nullptr;
-PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv = nullptr;
-PFNGLVERTEXATTRIB3SPROC glVertexAttrib3s = nullptr;
-PFNGLVERTEXATTRIB3SVPROC glVertexAttrib3sv = nullptr;
-PFNGLVERTEXATTRIB4NBVPROC glVertexAttrib4Nbv = nullptr;
-PFNGLVERTEXATTRIB4NIVPROC glVertexAttrib4Niv = nullptr;
-PFNGLVERTEXATTRIB4NSVPROC glVertexAttrib4Nsv = nullptr;
-PFNGLVERTEXATTRIB4NUBPROC glVertexAttrib4Nub = nullptr;
-PFNGLVERTEXATTRIB4NUBVPROC glVertexAttrib4Nubv = nullptr;
-PFNGLVERTEXATTRIB4NUIVPROC glVertexAttrib4Nuiv = nullptr;
-PFNGLVERTEXATTRIB4NUSVPROC glVertexAttrib4Nusv = nullptr;
-PFNGLVERTEXATTRIB4BVPROC glVertexAttrib4bv = nullptr;
-PFNGLVERTEXATTRIB4DPROC glVertexAttrib4d = nullptr;
-PFNGLVERTEXATTRIB4DVPROC glVertexAttrib4dv = nullptr;
-PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f = nullptr;
-PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv = nullptr;
-PFNGLVERTEXATTRIB4IVPROC glVertexAttrib4iv = nullptr;
-PFNGLVERTEXATTRIB4SPROC glVertexAttrib4s = nullptr;
-PFNGLVERTEXATTRIB4SVPROC glVertexAttrib4sv = nullptr;
-PFNGLVERTEXATTRIB4UBVPROC glVertexAttrib4ubv = nullptr;
-PFNGLVERTEXATTRIB4UIVPROC glVertexAttrib4uiv = nullptr;
-PFNGLVERTEXATTRIB4USVPROC glVertexAttrib4usv = nullptr;
-PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr;
-
-// GL_VERSION_2_1
-PFNGLUNIFORMMATRIX2X3FVPROC glUniformMatrix2x3fv = nullptr;
-PFNGLUNIFORMMATRIX3X2FVPROC glUniformMatrix3x2fv = nullptr;
-PFNGLUNIFORMMATRIX2X4FVPROC glUniformMatrix2x4fv = nullptr;
-PFNGLUNIFORMMATRIX4X2FVPROC glUniformMatrix4x2fv = nullptr;
-PFNGLUNIFORMMATRIX3X4FVPROC glUniformMatrix3x4fv = nullptr;
-PFNGLUNIFORMMATRIX4X3FVPROC glUniformMatrix4x3fv = nullptr;
-
-// GL_VERSION_3_0
-PFNGLCOLORMASKIPROC glColorMaski = nullptr;
-PFNGLGETBOOLEANI_VPROC glGetBooleani_v = nullptr;
-PFNGLGETINTEGERI_VPROC glGetIntegeri_v = nullptr;
-PFNGLENABLEIPROC glEnablei = nullptr;
-PFNGLDISABLEIPROC glDisablei = nullptr;
-PFNGLISENABLEDIPROC glIsEnabledi = nullptr;
-PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback = nullptr;
-PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback = nullptr;
-PFNGLBINDBUFFERRANGEPROC glBindBufferRange = nullptr;
-PFNGLBINDBUFFERBASEPROC glBindBufferBase = nullptr;
-PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings = nullptr;
-PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glGetTransformFeedbackVarying = nullptr;
-PFNGLCLAMPCOLORPROC glClampColor = nullptr;
-PFNGLBEGINCONDITIONALRENDERPROC glBeginConditionalRender = nullptr;
-PFNGLENDCONDITIONALRENDERPROC glEndConditionalRender = nullptr;
-PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer = nullptr;
-PFNGLGETVERTEXATTRIBIIVPROC glGetVertexAttribIiv = nullptr;
-PFNGLGETVERTEXATTRIBIUIVPROC glGetVertexAttribIuiv = nullptr;
-PFNGLVERTEXATTRIBI1IPROC glVertexAttribI1i = nullptr;
-PFNGLVERTEXATTRIBI2IPROC glVertexAttribI2i = nullptr;
-PFNGLVERTEXATTRIBI3IPROC glVertexAttribI3i = nullptr;
-PFNGLVERTEXATTRIBI4IPROC glVertexAttribI4i = nullptr;
-PFNGLVERTEXATTRIBI1UIPROC glVertexAttribI1ui = nullptr;
-PFNGLVERTEXATTRIBI2UIPROC glVertexAttribI2ui = nullptr;
-PFNGLVERTEXATTRIBI3UIPROC glVertexAttribI3ui = nullptr;
-PFNGLVERTEXATTRIBI4UIPROC glVertexAttribI4ui = nullptr;
-PFNGLVERTEXATTRIBI1IVPROC glVertexAttribI1iv = nullptr;
-PFNGLVERTEXATTRIBI2IVPROC glVertexAttribI2iv = nullptr;
-PFNGLVERTEXATTRIBI3IVPROC glVertexAttribI3iv = nullptr;
-PFNGLVERTEXATTRIBI4IVPROC glVertexAttribI4iv = nullptr;
-PFNGLVERTEXATTRIBI1UIVPROC glVertexAttribI1uiv = nullptr;
-PFNGLVERTEXATTRIBI2UIVPROC glVertexAttribI2uiv = nullptr;
-PFNGLVERTEXATTRIBI3UIVPROC glVertexAttribI3uiv = nullptr;
-PFNGLVERTEXATTRIBI4UIVPROC glVertexAttribI4uiv = nullptr;
-PFNGLVERTEXATTRIBI4BVPROC glVertexAttribI4bv = nullptr;
-PFNGLVERTEXATTRIBI4SVPROC glVertexAttribI4sv = nullptr;
-PFNGLVERTEXATTRIBI4UBVPROC glVertexAttribI4ubv = nullptr;
-PFNGLVERTEXATTRIBI4USVPROC glVertexAttribI4usv = nullptr;
-PFNGLGETUNIFORMUIVPROC glGetUniformuiv = nullptr;
-PFNGLBINDFRAGDATALOCATIONPROC glBindFragDataLocation = nullptr;
-PFNGLGETFRAGDATALOCATIONPROC glGetFragDataLocation = nullptr;
-PFNGLUNIFORM1UIPROC glUniform1ui = nullptr;
-PFNGLUNIFORM2UIPROC glUniform2ui = nullptr;
-PFNGLUNIFORM3UIPROC glUniform3ui = nullptr;
-PFNGLUNIFORM4UIPROC glUniform4ui = nullptr;
-PFNGLUNIFORM1UIVPROC glUniform1uiv = nullptr;
-PFNGLUNIFORM2UIVPROC glUniform2uiv = nullptr;
-PFNGLUNIFORM3UIVPROC glUniform3uiv = nullptr;
-PFNGLUNIFORM4UIVPROC glUniform4uiv = nullptr;
-PFNGLTEXPARAMETERIIVPROC glTexParameterIiv = nullptr;
-PFNGLTEXPARAMETERIUIVPROC glTexParameterIuiv = nullptr;
-PFNGLGETTEXPARAMETERIIVPROC glGetTexParameterIiv = nullptr;
-PFNGLGETTEXPARAMETERIUIVPROC glGetTexParameterIuiv = nullptr;
-PFNGLCLEARBUFFERIVPROC glClearBufferiv = nullptr;
-PFNGLCLEARBUFFERUIVPROC glClearBufferuiv = nullptr;
-PFNGLCLEARBUFFERFVPROC glClearBufferfv = nullptr;
-PFNGLCLEARBUFFERFIPROC glClearBufferfi = nullptr;
-PFNGLGETSTRINGIPROC glGetStringi = nullptr;
-PFNGLISRENDERBUFFERPROC glIsRenderbuffer = nullptr;
-PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer = nullptr;
-PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers = nullptr;
-PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers = nullptr;
-PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = nullptr;
-PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv = nullptr;
-PFNGLISFRAMEBUFFERPROC glIsFramebuffer = nullptr;
-PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = nullptr;
-PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = nullptr;
-PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = nullptr;
-PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus = nullptr;
-PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D = nullptr;
-PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = nullptr;
-PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D = nullptr;
-PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer = nullptr;
-PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv = nullptr;
-PFNGLGENERATEMIPMAPPROC glGenerateMipmap = nullptr;
-PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer = nullptr;
-PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample = nullptr;
-PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer = nullptr;
-PFNGLMAPBUFFERRANGEPROC glMapBufferRange = nullptr;
-PFNGLFLUSHMAPPEDBUFFERRANGEPROC glFlushMappedBufferRange = nullptr;
-PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr;
-PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = nullptr;
-PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr;
-PFNGLISVERTEXARRAYPROC glIsVertexArray = nullptr;
-
-// GL_VERSION_3_1
-PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced = nullptr;
-PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced = nullptr;
-PFNGLTEXBUFFERPROC glTexBuffer = nullptr;
-PFNGLPRIMITIVERESTARTINDEXPROC glPrimitiveRestartIndex = nullptr;
-PFNGLCOPYBUFFERSUBDATAPROC glCopyBufferSubData = nullptr;
-PFNGLGETUNIFORMINDICESPROC glGetUniformIndices = nullptr;
-PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv = nullptr;
-PFNGLGETACTIVEUNIFORMNAMEPROC glGetActiveUniformName = nullptr;
-PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex = nullptr;
-PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv = nullptr;
-PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glGetActiveUniformBlockName = nullptr;
-PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding = nullptr;
-
-// GL_VERSION_3_2
-PFNGLDRAWELEMENTSBASEVERTEXPROC glDrawElementsBaseVertex = nullptr;
-PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glDrawRangeElementsBaseVertex = nullptr;
-PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glDrawElementsInstancedBaseVertex = nullptr;
-PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glMultiDrawElementsBaseVertex = nullptr;
-PFNGLPROVOKINGVERTEXPROC glProvokingVertex = nullptr;
-PFNGLFENCESYNCPROC glFenceSync = nullptr;
-PFNGLISSYNCPROC glIsSync = nullptr;
-PFNGLDELETESYNCPROC glDeleteSync = nullptr;
-PFNGLCLIENTWAITSYNCPROC glClientWaitSync = nullptr;
-PFNGLWAITSYNCPROC glWaitSync = nullptr;
-PFNGLGETINTEGER64VPROC glGetInteger64v = nullptr;
-PFNGLGETSYNCIVPROC glGetSynciv = nullptr;
-PFNGLGETINTEGER64I_VPROC glGetInteger64i_v = nullptr;
-PFNGLGETBUFFERPARAMETERI64VPROC glGetBufferParameteri64v = nullptr;
-PFNGLFRAMEBUFFERTEXTUREPROC glFramebufferTexture = nullptr;
-PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample = nullptr;
-PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample = nullptr;
-PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv = nullptr;
-PFNGLSAMPLEMASKIPROC glSampleMaski = nullptr;
-
-// GL_VERSION_3_3
-PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glBindFragDataLocationIndexed = nullptr;
-PFNGLGETFRAGDATAINDEXPROC glGetFragDataIndex = nullptr;
-PFNGLGENSAMPLERSPROC glGenSamplers = nullptr;
-PFNGLDELETESAMPLERSPROC glDeleteSamplers = nullptr;
-PFNGLISSAMPLERPROC glIsSampler = nullptr;
-PFNGLBINDSAMPLERPROC glBindSampler = nullptr;
-PFNGLSAMPLERPARAMETERIPROC glSamplerParameteri = nullptr;
-PFNGLSAMPLERPARAMETERIVPROC glSamplerParameteriv = nullptr;
-PFNGLSAMPLERPARAMETERFPROC glSamplerParameterf = nullptr;
-PFNGLSAMPLERPARAMETERFVPROC glSamplerParameterfv = nullptr;
-PFNGLSAMPLERPARAMETERIIVPROC glSamplerParameterIiv = nullptr;
-PFNGLSAMPLERPARAMETERIUIVPROC glSamplerParameterIuiv = nullptr;
-PFNGLGETSAMPLERPARAMETERIVPROC glGetSamplerParameteriv = nullptr;
-PFNGLGETSAMPLERPARAMETERIIVPROC glGetSamplerParameterIiv = nullptr;
-PFNGLGETSAMPLERPARAMETERFVPROC glGetSamplerParameterfv = nullptr;
-PFNGLGETSAMPLERPARAMETERIUIVPROC glGetSamplerParameterIuiv = nullptr;
-PFNGLQUERYCOUNTERPROC glQueryCounter = nullptr;
-PFNGLGETQUERYOBJECTI64VPROC glGetQueryObjecti64v = nullptr;
-PFNGLGETQUERYOBJECTUI64VPROC glGetQueryObjectui64v = nullptr;
-PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor = nullptr;
-PFNGLVERTEXATTRIBP1UIPROC glVertexAttribP1ui = nullptr;
-PFNGLVERTEXATTRIBP1UIVPROC glVertexAttribP1uiv = nullptr;
-PFNGLVERTEXATTRIBP2UIPROC glVertexAttribP2ui = nullptr;
-PFNGLVERTEXATTRIBP2UIVPROC glVertexAttribP2uiv = nullptr;
-PFNGLVERTEXATTRIBP3UIPROC glVertexAttribP3ui = nullptr;
-PFNGLVERTEXATTRIBP3UIVPROC glVertexAttribP3uiv = nullptr;
-PFNGLVERTEXATTRIBP4UIPROC glVertexAttribP4ui = nullptr;
-PFNGLVERTEXATTRIBP4UIVPROC glVertexAttribP4uiv = nullptr;
-PFNGLVERTEXP2UIPROC glVertexP2ui = nullptr;
-PFNGLVERTEXP2UIVPROC glVertexP2uiv = nullptr;
-PFNGLVERTEXP3UIPROC glVertexP3ui = nullptr;
-PFNGLVERTEXP3UIVPROC glVertexP3uiv = nullptr;
-PFNGLVERTEXP4UIPROC glVertexP4ui = nullptr;
-PFNGLVERTEXP4UIVPROC glVertexP4uiv = nullptr;
-PFNGLTEXCOORDP1UIPROC glTexCoordP1ui = nullptr;
-PFNGLTEXCOORDP1UIVPROC glTexCoordP1uiv = nullptr;
-PFNGLTEXCOORDP2UIPROC glTexCoordP2ui = nullptr;
-PFNGLTEXCOORDP2UIVPROC glTexCoordP2uiv = nullptr;
-PFNGLTEXCOORDP3UIPROC glTexCoordP3ui = nullptr;
-PFNGLTEXCOORDP3UIVPROC glTexCoordP3uiv = nullptr;
-PFNGLTEXCOORDP4UIPROC glTexCoordP4ui = nullptr;
-PFNGLTEXCOORDP4UIVPROC glTexCoordP4uiv = nullptr;
-PFNGLMULTITEXCOORDP1UIPROC glMultiTexCoordP1ui = nullptr;
-PFNGLMULTITEXCOORDP1UIVPROC glMultiTexCoordP1uiv = nullptr;
-PFNGLMULTITEXCOORDP2UIPROC glMultiTexCoordP2ui = nullptr;
-PFNGLMULTITEXCOORDP2UIVPROC glMultiTexCoordP2uiv = nullptr;
-PFNGLMULTITEXCOORDP3UIPROC glMultiTexCoordP3ui = nullptr;
-PFNGLMULTITEXCOORDP3UIVPROC glMultiTexCoordP3uiv = nullptr;
-PFNGLMULTITEXCOORDP4UIPROC glMultiTexCoordP4ui = nullptr;
-PFNGLMULTITEXCOORDP4UIVPROC glMultiTexCoordP4uiv = nullptr;
-PFNGLNORMALP3UIPROC glNormalP3ui = nullptr;
-PFNGLNORMALP3UIVPROC glNormalP3uiv = nullptr;
-PFNGLCOLORP3UIPROC glColorP3ui = nullptr;
-PFNGLCOLORP3UIVPROC glColorP3uiv = nullptr;
-PFNGLCOLORP4UIPROC glColorP4ui = nullptr;
-PFNGLCOLORP4UIVPROC glColorP4uiv = nullptr;
-PFNGLSECONDARYCOLORP3UIPROC glSecondaryColorP3ui = nullptr;
-PFNGLSECONDARYCOLORP3UIVPROC glSecondaryColorP3uiv = nullptr;
-
-// GL_VERSION_4_0
-PFNGLMINSAMPLESHADINGPROC glMinSampleShading = nullptr;
-PFNGLBLENDEQUATIONIPROC glBlendEquationi = nullptr;
-PFNGLBLENDEQUATIONSEPARATEIPROC glBlendEquationSeparatei = nullptr;
-PFNGLBLENDFUNCIPROC glBlendFunci = nullptr;
-PFNGLBLENDFUNCSEPARATEIPROC glBlendFuncSeparatei = nullptr;
-PFNGLDRAWARRAYSINDIRECTPROC glDrawArraysIndirect = nullptr;
-PFNGLDRAWELEMENTSINDIRECTPROC glDrawElementsIndirect = nullptr;
-PFNGLUNIFORM1DPROC glUniform1d = nullptr;
-PFNGLUNIFORM2DPROC glUniform2d = nullptr;
-PFNGLUNIFORM3DPROC glUniform3d = nullptr;
-PFNGLUNIFORM4DPROC glUniform4d = nullptr;
-PFNGLUNIFORM1DVPROC glUniform1dv = nullptr;
-PFNGLUNIFORM2DVPROC glUniform2dv = nullptr;
-PFNGLUNIFORM3DVPROC glUniform3dv = nullptr;
-PFNGLUNIFORM4DVPROC glUniform4dv = nullptr;
-PFNGLUNIFORMMATRIX2DVPROC glUniformMatrix2dv = nullptr;
-PFNGLUNIFORMMATRIX3DVPROC glUniformMatrix3dv = nullptr;
-PFNGLUNIFORMMATRIX4DVPROC glUniformMatrix4dv = nullptr;
-PFNGLUNIFORMMATRIX2X3DVPROC glUniformMatrix2x3dv = nullptr;
-PFNGLUNIFORMMATRIX2X4DVPROC glUniformMatrix2x4dv = nullptr;
-PFNGLUNIFORMMATRIX3X2DVPROC glUniformMatrix3x2dv = nullptr;
-PFNGLUNIFORMMATRIX3X4DVPROC glUniformMatrix3x4dv = nullptr;
-PFNGLUNIFORMMATRIX4X2DVPROC glUniformMatrix4x2dv = nullptr;
-PFNGLUNIFORMMATRIX4X3DVPROC glUniformMatrix4x3dv = nullptr;
-PFNGLGETUNIFORMDVPROC glGetUniformdv = nullptr;
-PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC glGetSubroutineUniformLocation = nullptr;
-PFNGLGETSUBROUTINEINDEXPROC glGetSubroutineIndex = nullptr;
-PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC glGetActiveSubroutineUniformiv = nullptr;
-PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC glGetActiveSubroutineUniformName = nullptr;
-PFNGLGETACTIVESUBROUTINENAMEPROC glGetActiveSubroutineName = nullptr;
-PFNGLUNIFORMSUBROUTINESUIVPROC glUniformSubroutinesuiv = nullptr;
-PFNGLGETUNIFORMSUBROUTINEUIVPROC glGetUniformSubroutineuiv = nullptr;
-PFNGLGETPROGRAMSTAGEIVPROC glGetProgramStageiv = nullptr;
-PFNGLPATCHPARAMETERIPROC glPatchParameteri = nullptr;
-PFNGLPATCHPARAMETERFVPROC glPatchParameterfv = nullptr;
-PFNGLBINDTRANSFORMFEEDBACKPROC glBindTransformFeedback = nullptr;
-PFNGLDELETETRANSFORMFEEDBACKSPROC glDeleteTransformFeedbacks = nullptr;
-PFNGLGENTRANSFORMFEEDBACKSPROC glGenTransformFeedbacks = nullptr;
-PFNGLISTRANSFORMFEEDBACKPROC glIsTransformFeedback = nullptr;
-PFNGLPAUSETRANSFORMFEEDBACKPROC glPauseTransformFeedback = nullptr;
-PFNGLRESUMETRANSFORMFEEDBACKPROC glResumeTransformFeedback = nullptr;
-PFNGLDRAWTRANSFORMFEEDBACKPROC glDrawTransformFeedback = nullptr;
-PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glDrawTransformFeedbackStream = nullptr;
-PFNGLBEGINQUERYINDEXEDPROC glBeginQueryIndexed = nullptr;
-PFNGLENDQUERYINDEXEDPROC glEndQueryIndexed = nullptr;
-PFNGLGETQUERYINDEXEDIVPROC glGetQueryIndexediv = nullptr;
-
-// GL_VERSION_4_1
-PFNGLRELEASESHADERCOMPILERPROC glReleaseShaderCompiler = nullptr;
-PFNGLSHADERBINARYPROC glShaderBinary = nullptr;
-PFNGLGETSHADERPRECISIONFORMATPROC glGetShaderPrecisionFormat = nullptr;
-PFNGLDEPTHRANGEFPROC glDepthRangef = nullptr;
-PFNGLCLEARDEPTHFPROC glClearDepthf = nullptr;
-PFNGLGETPROGRAMBINARYPROC glGetProgramBinary = nullptr;
-PFNGLPROGRAMBINARYPROC glProgramBinary = nullptr;
-PFNGLPROGRAMPARAMETERIPROC glProgramParameteri = nullptr;
-PFNGLUSEPROGRAMSTAGESPROC glUseProgramStages = nullptr;
-PFNGLACTIVESHADERPROGRAMPROC glActiveShaderProgram = nullptr;
-PFNGLCREATESHADERPROGRAMVPROC glCreateShaderProgramv = nullptr;
-PFNGLBINDPROGRAMPIPELINEPROC glBindProgramPipeline = nullptr;
-PFNGLDELETEPROGRAMPIPELINESPROC glDeleteProgramPipelines = nullptr;
-PFNGLGENPROGRAMPIPELINESPROC glGenProgramPipelines = nullptr;
-PFNGLISPROGRAMPIPELINEPROC glIsProgramPipeline = nullptr;
-PFNGLGETPROGRAMPIPELINEIVPROC glGetProgramPipelineiv = nullptr;
-PFNGLPROGRAMUNIFORM1IPROC glProgramUniform1i = nullptr;
-PFNGLPROGRAMUNIFORM1IVPROC glProgramUniform1iv = nullptr;
-PFNGLPROGRAMUNIFORM1FPROC glProgramUniform1f = nullptr;
-PFNGLPROGRAMUNIFORM1FVPROC glProgramUniform1fv = nullptr;
-PFNGLPROGRAMUNIFORM1DPROC glProgramUniform1d = nullptr;
-PFNGLPROGRAMUNIFORM1DVPROC glProgramUniform1dv = nullptr;
-PFNGLPROGRAMUNIFORM1UIPROC glProgramUniform1ui = nullptr;
-PFNGLPROGRAMUNIFORM1UIVPROC glProgramUniform1uiv = nullptr;
-PFNGLPROGRAMUNIFORM2IPROC glProgramUniform2i = nullptr;
-PFNGLPROGRAMUNIFORM2IVPROC glProgramUniform2iv = nullptr;
-PFNGLPROGRAMUNIFORM2FPROC glProgramUniform2f = nullptr;
-PFNGLPROGRAMUNIFORM2FVPROC glProgramUniform2fv = nullptr;
-PFNGLPROGRAMUNIFORM2DPROC glProgramUniform2d = nullptr;
-PFNGLPROGRAMUNIFORM2DVPROC glProgramUniform2dv = nullptr;
-PFNGLPROGRAMUNIFORM2UIPROC glProgramUniform2ui = nullptr;
-PFNGLPROGRAMUNIFORM2UIVPROC glProgramUniform2uiv = nullptr;
-PFNGLPROGRAMUNIFORM3IPROC glProgramUniform3i = nullptr;
-PFNGLPROGRAMUNIFORM3IVPROC glProgramUniform3iv = nullptr;
-PFNGLPROGRAMUNIFORM3FPROC glProgramUniform3f = nullptr;
-PFNGLPROGRAMUNIFORM3FVPROC glProgramUniform3fv = nullptr;
-PFNGLPROGRAMUNIFORM3DPROC glProgramUniform3d = nullptr;
-PFNGLPROGRAMUNIFORM3DVPROC glProgramUniform3dv = nullptr;
-PFNGLPROGRAMUNIFORM3UIPROC glProgramUniform3ui = nullptr;
-PFNGLPROGRAMUNIFORM3UIVPROC glProgramUniform3uiv = nullptr;
-PFNGLPROGRAMUNIFORM4IPROC glProgramUniform4i = nullptr;
-PFNGLPROGRAMUNIFORM4IVPROC glProgramUniform4iv = nullptr;
-PFNGLPROGRAMUNIFORM4FPROC glProgramUniform4f = nullptr;
-PFNGLPROGRAMUNIFORM4FVPROC glProgramUniform4fv = nullptr;
-PFNGLPROGRAMUNIFORM4DPROC glProgramUniform4d = nullptr;
-PFNGLPROGRAMUNIFORM4DVPROC glProgramUniform4dv = nullptr;
-PFNGLPROGRAMUNIFORM4UIPROC glProgramUniform4ui = nullptr;
-PFNGLPROGRAMUNIFORM4UIVPROC glProgramUniform4uiv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX2FVPROC glProgramUniformMatrix2fv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX3FVPROC glProgramUniformMatrix3fv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX4FVPROC glProgramUniformMatrix4fv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX2DVPROC glProgramUniformMatrix2dv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX3DVPROC glProgramUniformMatrix3dv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX4DVPROC glProgramUniformMatrix4dv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glProgramUniformMatrix2x3fv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glProgramUniformMatrix3x2fv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glProgramUniformMatrix2x4fv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glProgramUniformMatrix4x2fv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glProgramUniformMatrix3x4fv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glProgramUniformMatrix4x3fv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC glProgramUniformMatrix2x3dv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC glProgramUniformMatrix3x2dv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC glProgramUniformMatrix2x4dv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC glProgramUniformMatrix4x2dv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC glProgramUniformMatrix3x4dv = nullptr;
-PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC glProgramUniformMatrix4x3dv = nullptr;
-PFNGLVALIDATEPROGRAMPIPELINEPROC glValidateProgramPipeline = nullptr;
-PFNGLGETPROGRAMPIPELINEINFOLOGPROC glGetProgramPipelineInfoLog = nullptr;
-PFNGLVERTEXATTRIBL1DPROC glVertexAttribL1d = nullptr;
-PFNGLVERTEXATTRIBL2DPROC glVertexAttribL2d = nullptr;
-PFNGLVERTEXATTRIBL3DPROC glVertexAttribL3d = nullptr;
-PFNGLVERTEXATTRIBL4DPROC glVertexAttribL4d = nullptr;
-PFNGLVERTEXATTRIBL1DVPROC glVertexAttribL1dv = nullptr;
-PFNGLVERTEXATTRIBL2DVPROC glVertexAttribL2dv = nullptr;
-PFNGLVERTEXATTRIBL3DVPROC glVertexAttribL3dv = nullptr;
-PFNGLVERTEXATTRIBL4DVPROC glVertexAttribL4dv = nullptr;
-PFNGLVERTEXATTRIBLPOINTERPROC glVertexAttribLPointer = nullptr;
-PFNGLGETVERTEXATTRIBLDVPROC glGetVertexAttribLdv = nullptr;
-PFNGLVIEWPORTARRAYVPROC glViewportArrayv = nullptr;
-PFNGLVIEWPORTINDEXEDFPROC glViewportIndexedf = nullptr;
-PFNGLVIEWPORTINDEXEDFVPROC glViewportIndexedfv = nullptr;
-PFNGLSCISSORARRAYVPROC glScissorArrayv = nullptr;
-PFNGLSCISSORINDEXEDPROC glScissorIndexed = nullptr;
-PFNGLSCISSORINDEXEDVPROC glScissorIndexedv = nullptr;
-PFNGLDEPTHRANGEARRAYVPROC glDepthRangeArrayv = nullptr;
-PFNGLDEPTHRANGEINDEXEDPROC glDepthRangeIndexed = nullptr;
-PFNGLGETFLOATI_VPROC glGetFloati_v = nullptr;
-PFNGLGETDOUBLEI_VPROC glGetDoublei_v = nullptr;
-
-// GL_VERSION_4_2
-PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glDrawArraysInstancedBaseInstance = nullptr;
-PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glDrawElementsInstancedBaseInstance = nullptr;
-PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glDrawElementsInstancedBaseVertexBaseInstance = nullptr;
-PFNGLGETINTERNALFORMATIVPROC glGetInternalformativ = nullptr;
-PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glGetActiveAtomicCounterBufferiv = nullptr;
-PFNGLBINDIMAGETEXTUREPROC glBindImageTexture = nullptr;
-PFNGLMEMORYBARRIERPROC glMemoryBarrier = nullptr;
-PFNGLTEXSTORAGE1DPROC glTexStorage1D = nullptr;
-PFNGLTEXSTORAGE2DPROC glTexStorage2D = nullptr;
-PFNGLTEXSTORAGE3DPROC glTexStorage3D = nullptr;
-PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glDrawTransformFeedbackInstanced = nullptr;
-PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glDrawTransformFeedbackStreamInstanced = nullptr;
-
-// GL_VERSION_4_3
-PFNGLCLEARBUFFERDATAPROC glClearBufferData = nullptr;
-PFNGLCLEARBUFFERSUBDATAPROC glClearBufferSubData = nullptr;
-PFNGLDISPATCHCOMPUTEPROC glDispatchCompute = nullptr;
-PFNGLDISPATCHCOMPUTEINDIRECTPROC glDispatchComputeIndirect = nullptr;
-PFNGLCOPYIMAGESUBDATAPROC glCopyImageSubData = nullptr;
-PFNGLFRAMEBUFFERPARAMETERIPROC glFramebufferParameteri = nullptr;
-PFNGLGETFRAMEBUFFERPARAMETERIVPROC glGetFramebufferParameteriv = nullptr;
-PFNGLGETINTERNALFORMATI64VPROC glGetInternalformati64v = nullptr;
-PFNGLINVALIDATETEXSUBIMAGEPROC glInvalidateTexSubImage = nullptr;
-PFNGLINVALIDATETEXIMAGEPROC glInvalidateTexImage = nullptr;
-PFNGLINVALIDATEBUFFERSUBDATAPROC glInvalidateBufferSubData = nullptr;
-PFNGLINVALIDATEBUFFERDATAPROC glInvalidateBufferData = nullptr;
-PFNGLINVALIDATEFRAMEBUFFERPROC glInvalidateFramebuffer = nullptr;
-PFNGLINVALIDATESUBFRAMEBUFFERPROC glInvalidateSubFramebuffer = nullptr;
-PFNGLMULTIDRAWARRAYSINDIRECTPROC glMultiDrawArraysIndirect = nullptr;
-PFNGLMULTIDRAWELEMENTSINDIRECTPROC glMultiDrawElementsIndirect = nullptr;
-PFNGLGETPROGRAMINTERFACEIVPROC glGetProgramInterfaceiv = nullptr;
-PFNGLGETPROGRAMRESOURCEINDEXPROC glGetProgramResourceIndex = nullptr;
-PFNGLGETPROGRAMRESOURCENAMEPROC glGetProgramResourceName = nullptr;
-PFNGLGETPROGRAMRESOURCEIVPROC glGetProgramResourceiv = nullptr;
-PFNGLGETPROGRAMRESOURCELOCATIONPROC glGetProgramResourceLocation = nullptr;
-PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC glGetProgramResourceLocationIndex = nullptr;
-PFNGLSHADERSTORAGEBLOCKBINDINGPROC glShaderStorageBlockBinding = nullptr;
-PFNGLTEXBUFFERRANGEPROC glTexBufferRange = nullptr;
-PFNGLTEXSTORAGE2DMULTISAMPLEPROC glTexStorage2DMultisample = nullptr;
-PFNGLTEXSTORAGE3DMULTISAMPLEPROC glTexStorage3DMultisample = nullptr;
-PFNGLTEXTUREVIEWPROC glTextureView = nullptr;
-PFNGLBINDVERTEXBUFFERPROC glBindVertexBuffer = nullptr;
-PFNGLVERTEXATTRIBFORMATPROC glVertexAttribFormat = nullptr;
-PFNGLVERTEXATTRIBIFORMATPROC glVertexAttribIFormat = nullptr;
-PFNGLVERTEXATTRIBLFORMATPROC glVertexAttribLFormat = nullptr;
-PFNGLVERTEXATTRIBBINDINGPROC glVertexAttribBinding = nullptr;
-PFNGLVERTEXBINDINGDIVISORPROC glVertexBindingDivisor = nullptr;
-PFNGLDEBUGMESSAGECONTROLPROC glDebugMessageControl = nullptr;
-PFNGLDEBUGMESSAGEINSERTPROC glDebugMessageInsert = nullptr;
-PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback = nullptr;
-PFNGLGETDEBUGMESSAGELOGPROC glGetDebugMessageLog = nullptr;
-PFNGLPUSHDEBUGGROUPPROC glPushDebugGroup = nullptr;
-PFNGLPOPDEBUGGROUPPROC glPopDebugGroup = nullptr;
-PFNGLOBJECTLABELPROC glObjectLabel = nullptr;
-PFNGLGETOBJECTLABELPROC glGetObjectLabel = nullptr;
-PFNGLOBJECTPTRLABELPROC glObjectPtrLabel = nullptr;
-PFNGLGETOBJECTPTRLABELPROC glGetObjectPtrLabel = nullptr;
-
-// GL_VERSION_4_4
-PFNGLBUFFERSTORAGEPROC glBufferStorage = nullptr;
-PFNGLCLEARTEXIMAGEPROC glClearTexImage = nullptr;
-PFNGLCLEARTEXSUBIMAGEPROC glClearTexSubImage = nullptr;
-PFNGLBINDBUFFERSBASEPROC glBindBuffersBase = nullptr;
-PFNGLBINDBUFFERSRANGEPROC glBindBuffersRange = nullptr;
-PFNGLBINDTEXTURESPROC glBindTextures = nullptr;
-PFNGLBINDSAMPLERSPROC glBindSamplers = nullptr;
-PFNGLBINDIMAGETEXTURESPROC glBindImageTextures = nullptr;
-PFNGLBINDVERTEXBUFFERSPROC glBindVertexBuffers = nullptr;
-
-// GL_VERSION_4_5
-PFNGLCLIPCONTROLPROC glClipControl = nullptr;
-PFNGLCREATETRANSFORMFEEDBACKSPROC glCreateTransformFeedbacks = nullptr;
-PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glTransformFeedbackBufferBase = nullptr;
-PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glTransformFeedbackBufferRange = nullptr;
-PFNGLGETTRANSFORMFEEDBACKIVPROC glGetTransformFeedbackiv = nullptr;
-PFNGLGETTRANSFORMFEEDBACKI_VPROC glGetTransformFeedbacki_v = nullptr;
-PFNGLGETTRANSFORMFEEDBACKI64_VPROC glGetTransformFeedbacki64_v = nullptr;
-PFNGLCREATEBUFFERSPROC glCreateBuffers = nullptr;
-PFNGLNAMEDBUFFERSTORAGEPROC glNamedBufferStorage = nullptr;
-PFNGLNAMEDBUFFERDATAPROC glNamedBufferData = nullptr;
-PFNGLNAMEDBUFFERSUBDATAPROC glNamedBufferSubData = nullptr;
-PFNGLCOPYNAMEDBUFFERSUBDATAPROC glCopyNamedBufferSubData = nullptr;
-PFNGLCLEARNAMEDBUFFERDATAPROC glClearNamedBufferData = nullptr;
-PFNGLCLEARNAMEDBUFFERSUBDATAPROC glClearNamedBufferSubData = nullptr;
-PFNGLMAPNAMEDBUFFERPROC glMapNamedBuffer = nullptr;
-PFNGLMAPNAMEDBUFFERRANGEPROC glMapNamedBufferRange = nullptr;
-PFNGLUNMAPNAMEDBUFFERPROC glUnmapNamedBuffer = nullptr;
-PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glFlushMappedNamedBufferRange = nullptr;
-PFNGLGETNAMEDBUFFERPARAMETERIVPROC glGetNamedBufferParameteriv = nullptr;
-PFNGLGETNAMEDBUFFERPARAMETERI64VPROC glGetNamedBufferParameteri64v = nullptr;
-PFNGLGETNAMEDBUFFERPOINTERVPROC glGetNamedBufferPointerv = nullptr;
-PFNGLGETNAMEDBUFFERSUBDATAPROC glGetNamedBufferSubData = nullptr;
-PFNGLCREATEFRAMEBUFFERSPROC glCreateFramebuffers = nullptr;
-PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glNamedFramebufferRenderbuffer = nullptr;
-PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glNamedFramebufferParameteri = nullptr;
-PFNGLNAMEDFRAMEBUFFERTEXTUREPROC glNamedFramebufferTexture = nullptr;
-PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glNamedFramebufferTextureLayer = nullptr;
-PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glNamedFramebufferDrawBuffer = nullptr;
-PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glNamedFramebufferDrawBuffers = nullptr;
-PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glNamedFramebufferReadBuffer = nullptr;
-PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glInvalidateNamedFramebufferData = nullptr;
-PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glInvalidateNamedFramebufferSubData = nullptr;
-PFNGLCLEARNAMEDFRAMEBUFFERIVPROC glClearNamedFramebufferiv = nullptr;
-PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glClearNamedFramebufferuiv = nullptr;
-PFNGLCLEARNAMEDFRAMEBUFFERFVPROC glClearNamedFramebufferfv = nullptr;
-PFNGLCLEARNAMEDFRAMEBUFFERFIPROC glClearNamedFramebufferfi = nullptr;
-PFNGLBLITNAMEDFRAMEBUFFERPROC glBlitNamedFramebuffer = nullptr;
-PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glCheckNamedFramebufferStatus = nullptr;
-PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glGetNamedFramebufferParameteriv = nullptr;
-PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetNamedFramebufferAttachmentParameteriv = nullptr;
-PFNGLCREATERENDERBUFFERSPROC glCreateRenderbuffers = nullptr;
-PFNGLNAMEDRENDERBUFFERSTORAGEPROC glNamedRenderbufferStorage = nullptr;
-PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glNamedRenderbufferStorageMultisample = nullptr;
-PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glGetNamedRenderbufferParameteriv = nullptr;
-PFNGLCREATETEXTURESPROC glCreateTextures = nullptr;
-PFNGLTEXTUREBUFFERPROC glTextureBuffer = nullptr;
-PFNGLTEXTUREBUFFERRANGEPROC glTextureBufferRange = nullptr;
-PFNGLTEXTURESTORAGE1DPROC glTextureStorage1D = nullptr;
-PFNGLTEXTURESTORAGE2DPROC glTextureStorage2D = nullptr;
-PFNGLTEXTURESTORAGE3DPROC glTextureStorage3D = nullptr;
-PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glTextureStorage2DMultisample = nullptr;
-PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glTextureStorage3DMultisample = nullptr;
-PFNGLTEXTURESUBIMAGE1DPROC glTextureSubImage1D = nullptr;
-PFNGLTEXTURESUBIMAGE2DPROC glTextureSubImage2D = nullptr;
-PFNGLTEXTURESUBIMAGE3DPROC glTextureSubImage3D = nullptr;
-PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glCompressedTextureSubImage1D = nullptr;
-PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glCompressedTextureSubImage2D = nullptr;
-PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glCompressedTextureSubImage3D = nullptr;
-PFNGLCOPYTEXTURESUBIMAGE1DPROC glCopyTextureSubImage1D = nullptr;
-PFNGLCOPYTEXTURESUBIMAGE2DPROC glCopyTextureSubImage2D = nullptr;
-PFNGLCOPYTEXTURESUBIMAGE3DPROC glCopyTextureSubImage3D = nullptr;
-PFNGLTEXTUREPARAMETERFPROC glTextureParameterf = nullptr;
-PFNGLTEXTUREPARAMETERFVPROC glTextureParameterfv = nullptr;
-PFNGLTEXTUREPARAMETERIPROC glTextureParameteri = nullptr;
-PFNGLTEXTUREPARAMETERIIVPROC glTextureParameterIiv = nullptr;
-PFNGLTEXTUREPARAMETERIUIVPROC glTextureParameterIuiv = nullptr;
-PFNGLTEXTUREPARAMETERIVPROC glTextureParameteriv = nullptr;
-PFNGLGENERATETEXTUREMIPMAPPROC glGenerateTextureMipmap = nullptr;
-PFNGLBINDTEXTUREUNITPROC glBindTextureUnit = nullptr;
-PFNGLGETTEXTUREIMAGEPROC glGetTextureImage = nullptr;
-PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glGetCompressedTextureImage = nullptr;
-PFNGLGETTEXTURELEVELPARAMETERFVPROC glGetTextureLevelParameterfv = nullptr;
-PFNGLGETTEXTURELEVELPARAMETERIVPROC glGetTextureLevelParameteriv = nullptr;
-PFNGLGETTEXTUREPARAMETERFVPROC glGetTextureParameterfv = nullptr;
-PFNGLGETTEXTUREPARAMETERIIVPROC glGetTextureParameterIiv = nullptr;
-PFNGLGETTEXTUREPARAMETERIUIVPROC glGetTextureParameterIuiv = nullptr;
-PFNGLGETTEXTUREPARAMETERIVPROC glGetTextureParameteriv = nullptr;
-PFNGLCREATEVERTEXARRAYSPROC glCreateVertexArrays = nullptr;
-PFNGLDISABLEVERTEXARRAYATTRIBPROC glDisableVertexArrayAttrib = nullptr;
-PFNGLENABLEVERTEXARRAYATTRIBPROC glEnableVertexArrayAttrib = nullptr;
-PFNGLVERTEXARRAYELEMENTBUFFERPROC glVertexArrayElementBuffer = nullptr;
-PFNGLVERTEXARRAYVERTEXBUFFERPROC glVertexArrayVertexBuffer = nullptr;
-PFNGLVERTEXARRAYVERTEXBUFFERSPROC glVertexArrayVertexBuffers = nullptr;
-PFNGLVERTEXARRAYATTRIBBINDINGPROC glVertexArrayAttribBinding = nullptr;
-PFNGLVERTEXARRAYATTRIBFORMATPROC glVertexArrayAttribFormat = nullptr;
-PFNGLVERTEXARRAYATTRIBIFORMATPROC glVertexArrayAttribIFormat = nullptr;
-PFNGLVERTEXARRAYATTRIBLFORMATPROC glVertexArrayAttribLFormat = nullptr;
-PFNGLVERTEXARRAYBINDINGDIVISORPROC glVertexArrayBindingDivisor = nullptr;
-PFNGLGETVERTEXARRAYIVPROC glGetVertexArrayiv = nullptr;
-PFNGLGETVERTEXARRAYINDEXEDIVPROC glGetVertexArrayIndexediv = nullptr;
-PFNGLGETVERTEXARRAYINDEXED64IVPROC glGetVertexArrayIndexed64iv = nullptr;
-PFNGLCREATESAMPLERSPROC glCreateSamplers = nullptr;
-PFNGLCREATEPROGRAMPIPELINESPROC glCreateProgramPipelines = nullptr;
-PFNGLCREATEQUERIESPROC glCreateQueries = nullptr;
-PFNGLGETQUERYBUFFEROBJECTI64VPROC glGetQueryBufferObjecti64v = nullptr;
-PFNGLGETQUERYBUFFEROBJECTIVPROC glGetQueryBufferObjectiv = nullptr;
-PFNGLGETQUERYBUFFEROBJECTUI64VPROC glGetQueryBufferObjectui64v = nullptr;
-PFNGLGETQUERYBUFFEROBJECTUIVPROC glGetQueryBufferObjectuiv = nullptr;
-PFNGLMEMORYBARRIERBYREGIONPROC glMemoryBarrierByRegion = nullptr;
-PFNGLGETTEXTURESUBIMAGEPROC glGetTextureSubImage = nullptr;
-PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glGetCompressedTextureSubImage = nullptr;
-PFNGLGETGRAPHICSRESETSTATUSPROC glGetGraphicsResetStatus = nullptr;
-PFNGLGETNCOMPRESSEDTEXIMAGEPROC glGetnCompressedTexImage = nullptr;
-PFNGLGETNTEXIMAGEPROC glGetnTexImage = nullptr;
-PFNGLGETNUNIFORMDVPROC glGetnUniformdv = nullptr;
-PFNGLGETNUNIFORMFVPROC glGetnUniformfv = nullptr;
-PFNGLGETNUNIFORMIVPROC glGetnUniformiv = nullptr;
-PFNGLGETNUNIFORMUIVPROC glGetnUniformuiv = nullptr;
-PFNGLREADNPIXELSPROC glReadnPixels = nullptr;
-PFNGLGETNMAPDVPROC glGetnMapdv = nullptr;
-PFNGLGETNMAPFVPROC glGetnMapfv = nullptr;
-PFNGLGETNMAPIVPROC glGetnMapiv = nullptr;
-PFNGLGETNPIXELMAPFVPROC glGetnPixelMapfv = nullptr;
-PFNGLGETNPIXELMAPUIVPROC glGetnPixelMapuiv = nullptr;
-PFNGLGETNPIXELMAPUSVPROC glGetnPixelMapusv = nullptr;
-PFNGLGETNPOLYGONSTIPPLEPROC glGetnPolygonStipple = nullptr;
-PFNGLGETNCOLORTABLEPROC glGetnColorTable = nullptr;
-PFNGLGETNCONVOLUTIONFILTERPROC glGetnConvolutionFilter = nullptr;
-PFNGLGETNSEPARABLEFILTERPROC glGetnSeparableFilter = nullptr;
-PFNGLGETNHISTOGRAMPROC glGetnHistogram = nullptr;
-PFNGLGETNMINMAXPROC glGetnMinmax = nullptr;
-PFNGLTEXTUREBARRIERPROC glTextureBarrier = nullptr;
-
-// GL_VERSION_4_6
-PFNGLSPECIALIZESHADERPROC glSpecializeShader = nullptr;
-PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC glMultiDrawArraysIndirectCount = nullptr;
-PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC glMultiDrawElementsIndirectCount = nullptr;
-PFNGLPOLYGONOFFSETCLAMPPROC glPolygonOffsetClamp = nullptr;
-
-#endif
-
-LLGLManager gGLManager;
-
-LLGLManager::LLGLManager() :
- mInited(false),
- mIsDisabled(false),
- mMaxSamples(0),
- mNumTextureImageUnits(1),
- mMaxSampleMaskWords(0),
- mMaxColorTextureSamples(0),
- mMaxDepthTextureSamples(0),
- mMaxIntegerSamples(0),
- mIsAMD(false),
- mIsNVIDIA(false),
- mIsIntel(false),
-#if LL_DARWIN
- mIsMobileGF(false),
-#endif
- mHasRequirements(true),
- mDriverVersionMajor(1),
- mDriverVersionMinor(0),
- mDriverVersionRelease(0),
- mGLVersion(1.0f),
- mGLSLVersionMajor(0),
- mGLSLVersionMinor(0),
- mVRAM(0),
- mGLMaxVertexRange(0),
- mGLMaxIndexRange(0)
-{
-}
-
-//---------------------------------------------------------------------
-// Global initialization for GL
-//---------------------------------------------------------------------
-void LLGLManager::initWGL()
-{
-#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_ARB_create_context",gGLHExts.mSysExts))
- {
- GLH_EXT_NAME(wglCreateContextAttribsARB) = (PFNWGLCREATECONTEXTATTRIBSARBPROC)GLH_EXT_GET_PROC_ADDRESS("wglCreateContextAttribsARB");
- }
- else
- {
- LL_WARNS("RenderInit") << "No ARB create context extensions" << LL_ENDL;
- }
-
- // For retreiving information per AMD adapter,
- // because we can't trust curently selected/default one when there are multiple
- mHasAMDAssociations = ExtensionExists("WGL_AMD_gpu_association", gGLHExts.mSysExts);
- if (mHasAMDAssociations)
- {
- GLH_EXT_NAME(wglGetGPUIDsAMD) = (PFNWGLGETGPUIDSAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetGPUIDsAMD");
- GLH_EXT_NAME(wglGetGPUInfoAMD) = (PFNWGLGETGPUINFOAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetGPUInfoAMD");
- }
-
- 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;
- }
-#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;
- }
-
-#if 0 && LL_WINDOWS
- if (!glGetStringi)
- {
- glGetStringi = (PFNGLGETSTRINGIPROC) GLH_EXT_GET_PROC_ADDRESS("glGetStringi");
- }
-
- //reload extensions string (may have changed after using wglCreateContextAttrib)
- if (glGetStringi)
- {
- std::stringstream str;
-
- GLint count = 0;
- glGetIntegerv(GL_NUM_EXTENSIONS, &count);
- for (GLint i = 0; i < count; ++i)
- {
- std::string ext = ll_safe_string((const char*) glGetStringi(GL_EXTENSIONS, i));
- str << ext << " ";
- LL_DEBUGS("GLExtensions") << ext << LL_ENDL;
- }
-
- {
- PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = 0;
- wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
- if(wglGetExtensionsStringARB)
- {
- str << (const char*) wglGetExtensionsStringARB(wglGetCurrentDC());
- }
- }
-
- free(gGLHExts.mSysExts);
- std::string extensions = str.str();
- gGLHExts.mSysExts = strdup(extensions.c_str());
- }
-#endif
-
- // Extract video card strings and convert to upper case to
- // work around driver-to-driver variation in capitalization.
- mGLVendor = ll_safe_string((const char *)glGetString(GL_VENDOR));
- LLStringUtil::toUpper(mGLVendor);
-
- mGLRenderer = ll_safe_string((const char *)glGetString(GL_RENDERER));
- LLStringUtil::toUpper(mGLRenderer);
-
- parse_gl_version( &mDriverVersionMajor,
- &mDriverVersionMinor,
- &mDriverVersionRelease,
- &mDriverVersionVendorString,
- &mGLVersionString);
-
- mGLVersion = mDriverVersionMajor + mDriverVersionMinor * .1f;
-
- if (mGLVersion >= 2.f)
- {
- parse_glsl_version(mGLSLVersionMajor, mGLSLVersionMinor);
-
-#if 0 && LL_DARWIN
- // TODO maybe switch to using a core profile for GL 3.2?
- // https://stackoverflow.com/a/19868861
- //never use GLSL greater than 1.20 on OSX
- if (mGLSLVersionMajor > 1 || mGLSLVersionMinor > 30)
- {
- mGLSLVersionMajor = 1;
- mGLSLVersionMinor = 30;
- }
-#endif
- }
-
- if (mGLVersion >= 2.1f && LLImageGL::sCompressTextures)
- { //use texture compression
- glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST);
- }
- else
- { //GL version is < 3.0, always disable texture compression
- LLImageGL::sCompressTextures = false;
- }
-
- // Trailing space necessary to keep "nVidia Corpor_ati_on" cards
- // from being recognized as ATI.
- // NOTE: AMD has been pretty good about not breaking this check, do not rename without good reason
- if (mGLVendor.substr(0,4) == "ATI ")
- {
- mGLVendorShort = "AMD";
- // *TODO: Fix this?
- mIsAMD = true;
- }
- else if (mGLVendor.find("NVIDIA ") != std::string::npos)
- {
- mGLVendorShort = "NVIDIA";
- mIsNVIDIA = 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();
-
- S32 old_vram = mVRAM;
- mVRAM = 0;
-
-#if LL_WINDOWS
- if (mHasAMDAssociations)
- {
- GLuint gl_gpus_count = wglGetGPUIDsAMD(0, 0);
- if (gl_gpus_count > 0)
- {
- GLuint* ids = new GLuint[gl_gpus_count];
- wglGetGPUIDsAMD(gl_gpus_count, ids);
-
- GLuint mem_mb = 0;
- for (U32 i = 0; i < gl_gpus_count; i++)
- {
- wglGetGPUInfoAMD(ids[i],
- WGL_GPU_RAM_AMD,
- GL_UNSIGNED_INT,
- sizeof(GLuint),
- &mem_mb);
- if (mVRAM < mem_mb)
- {
- // basically pick the best AMD and trust driver/OS to know to switch
- mVRAM = mem_mb;
- }
- }
- }
- if (mVRAM != 0)
- {
- LL_WARNS("RenderInit") << "VRAM Detected (AMDAssociations):" << mVRAM << LL_ENDL;
- }
- }
-#endif
-
-#if LL_WINDOWS
- if (mVRAM < 256)
- {
- // Something likely went wrong using the above extensions
- // try WMI first and fall back to old method (from dxdiag) if all else fails
- // Function will check all GPUs WMI knows of and will pick up the one with most
- // memory. We need to check all GPUs because system can switch active GPU to
- // weaker one, to preserve power when not under load.
- S32 mem = LLDXHardware::getMBVideoMemoryViaWMI();
- if (mem != 0)
- {
- mVRAM = mem;
- LL_WARNS("RenderInit") << "VRAM Detected (WMI):" << mVRAM<< LL_ENDL;
- }
- }
-#endif
-
- if (mVRAM < 256 && old_vram > 0)
- {
- // fall back to old method
- // Note: on Windows value will be from LLDXHardware.
- // Either received via dxdiag or via WMI by id from dxdiag.
- mVRAM = old_vram;
- }
-
- glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mNumTextureImageUnits);
- glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &mMaxColorTextureSamples);
- glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &mMaxDepthTextureSamples);
- glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &mMaxIntegerSamples);
- glGetIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &mMaxSampleMaskWords);
- glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples);
-
- if (mGLVersion >= 4.59f)
- {
- glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &mMaxAnisotropy);
- }
-
- initGLStates();
-
- return true;
-}
-
-void LLGLManager::getGLInfo(LLSD& info)
-{
- if (gHeadlessClient)
- {
- info["GLInfo"]["GLVendor"] = HEADLESS_VENDOR_STRING;
- info["GLInfo"]["GLRenderer"] = HEADLESS_RENDERER_STRING;
- info["GLInfo"]["GLVersion"] = HEADLESS_VERSION_STRING;
- return;
- }
- else
- {
- info["GLInfo"]["GLVendor"] = ll_safe_string((const char *)glGetString(GL_VENDOR));
- info["GLInfo"]["GLRenderer"] = ll_safe_string((const char *)glGetString(GL_RENDERER));
- info["GLInfo"]["GLVersion"] = ll_safe_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;
-
- if (gHeadlessClient)
- {
- info_str += std::string("GL_VENDOR ") + HEADLESS_VENDOR_STRING + std::string("\n");
- info_str += std::string("GL_RENDERER ") + HEADLESS_RENDERER_STRING + std::string("\n");
- info_str += std::string("GL_VERSION ") + HEADLESS_VERSION_STRING + std::string("\n");
- }
- else
- {
- 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
- std::string all_exts= ll_safe_string(((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()
-{
- if (gHeadlessClient)
- {
- LL_INFOS("RenderInit") << "GL_VENDOR: " << HEADLESS_VENDOR_STRING << LL_ENDL;
- LL_INFOS("RenderInit") << "GL_RENDERER: " << HEADLESS_RENDERER_STRING << LL_ENDL;
- LL_INFOS("RenderInit") << "GL_VERSION: " << HEADLESS_VERSION_STRING << LL_ENDL;
- }
- else
- {
- LL_INFOS("RenderInit") << "GL_VENDOR: " << ll_safe_string((const char *)glGetString(GL_VENDOR)) << LL_ENDL;
- LL_INFOS("RenderInit") << "GL_RENDERER: " << ll_safe_string((const char *)glGetString(GL_RENDERER)) << LL_ENDL;
- LL_INFOS("RenderInit") << "GL_VERSION: " << ll_safe_string((const char *)glGetString(GL_VERSION)) << LL_ENDL;
- }
-
-#if !LL_MESA_HEADLESS
- std::string all_exts= ll_safe_string(((const char *)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;
- if (gHeadlessClient)
- {
- gl_string = HEADLESS_VENDOR_STRING + " " + HEADLESS_RENDERER_STRING;
- }
- else
- {
- gl_string = ll_safe_string((char*)glGetString(GL_VENDOR)) + " " + ll_safe_string((char*)glGetString(GL_RENDERER));
- }
- return gl_string;
-}
-
-void LLGLManager::asLLSD(LLSD& info)
-{
- // Currently these are duplicates of fields in "system".
- info["gpu_vendor"] = mGLVendorShort;
- info["gpu_version"] = mDriverVersionVendorString;
- info["opengl_version"] = mGLVersionString;
-
- info["vram"] = mVRAM;
-
- // OpenGL limits
- info["max_samples"] = mMaxSamples;
- info["num_texture_image_units"] = mNumTextureImageUnits;
- info["max_sample_mask_words"] = mMaxSampleMaskWords;
- info["max_color_texture_samples"] = mMaxColorTextureSamples;
- info["max_depth_texture_samples"] = mMaxDepthTextureSamples;
- info["max_integer_samples"] = mMaxIntegerSamples;
- info["max_vertex_range"] = mGLMaxVertexRange;
- info["max_index_range"] = mGLMaxIndexRange;
- info["max_texture_size"] = mGLMaxTextureSize;
-
- // Which vendor
- info["is_ati"] = mIsAMD; // note, do not rename is_ati to is_amd without coordinating with DW
- info["is_nvidia"] = mIsNVIDIA;
- info["is_intel"] = mIsIntel;
-
- info["gl_renderer"] = mGLRenderer;
-}
-
-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_DARWIN
- GLint num_extensions = 0;
- std::string all_extensions{""};
- glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
- for(GLint i = 0; i < num_extensions; ++i) {
- char const * extension = (char const *)glGetStringi(GL_EXTENSIONS, i);
- all_extensions += extension;
- all_extensions += ' ';
- }
- if (num_extensions)
- {
- all_extensions += "GL_ARB_multitexture GL_ARB_texture_cube_map GL_ARB_texture_compression "; // These are in 3.2 core, but not listed by OSX
- gGLHExts.mSysExts = strdup(all_extensions.data());
- }
-#endif
-
- // NOTE: version checks against mGLVersion should bias down by 0.01 because of F32 errors
-
- // OpenGL 4.x capabilities
- mHasCubeMapArray = mGLVersion >= 3.99f;
- mHasTransformFeedback = mGLVersion >= 3.99f;
- mHasDebugOutput = mGLVersion >= 4.29f;
-
- // Misc
- glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, (GLint*) &mGLMaxVertexRange);
- glGetIntegerv(GL_MAX_ELEMENTS_INDICES, (GLint*) &mGLMaxIndexRange);
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint*) &mGLMaxTextureSize);
-
- mInited = true;
-
-#if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS
- LL_DEBUGS("RenderInit") << "GL Probe: Getting symbols" << LL_ENDL;
-
-#if LL_WINDOWS
- // WGL_AMD_gpu_association
- wglGetGPUIDsAMD = (PFNWGLGETGPUIDSAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetGPUIDsAMD");
- wglGetGPUInfoAMD = (PFNWGLGETGPUINFOAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetGPUInfoAMD");
- wglGetContextGPUIDAMD = (PFNWGLGETCONTEXTGPUIDAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetContextGPUIDAMD");
- wglCreateAssociatedContextAMD = (PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglCreateAssociatedContextAMD");
- wglCreateAssociatedContextAttribsAMD = (PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglCreateAssociatedContextAttribsAMD");
- wglDeleteAssociatedContextAMD = (PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglDeleteAssociatedContextAMD");
- wglMakeAssociatedContextCurrentAMD = (PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglMakeAssociatedContextCurrentAMD");
- wglGetCurrentAssociatedContextAMD = (PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetCurrentAssociatedContextAMD");
- wglBlitContextFramebufferAMD = (PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglBlitContextFramebufferAMD");
-
- // WGL_EXT_swap_control
- wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)GLH_EXT_GET_PROC_ADDRESS("wglSwapIntervalEXT");
- wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetSwapIntervalEXT");
-
- // WGL_ARB_create_context
- wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)GLH_EXT_GET_PROC_ADDRESS("wglCreateContextAttribsARB");
-#endif
-
-
- // Load entire OpenGL API through GetProcAddress, leaving sections beyond mGLVersion unloaded
-
- // GL_VERSION_1_2
- if (mGLVersion < 1.19f)
- {
- return;
- }
- glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawRangeElements");
- glTexImage3D = (PFNGLTEXIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glTexImage3D");
- glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glTexSubImage3D");
- glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyTexSubImage3D");
-
-
- // GL_VERSION_1_3
- if (mGLVersion < 1.29f)
- {
- return;
- }
- glActiveTexture = (PFNGLACTIVETEXTUREPROC)GLH_EXT_GET_PROC_ADDRESS("glActiveTexture");
- glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glSampleCoverage");
- glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTexImage3D");
- glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTexImage2D");
- glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTexImage1D");
- glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTexSubImage3D");
- glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTexSubImage2D");
- glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTexSubImage1D");
- glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetCompressedTexImage");
- glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC)GLH_EXT_GET_PROC_ADDRESS("glClientActiveTexture");
- glMultiTexCoord1d = (PFNGLMULTITEXCOORD1DPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1d");
- glMultiTexCoord1dv = (PFNGLMULTITEXCOORD1DVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1dv");
- glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1f");
- glMultiTexCoord1fv = (PFNGLMULTITEXCOORD1FVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1fv");
- glMultiTexCoord1i = (PFNGLMULTITEXCOORD1IPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1i");
- glMultiTexCoord1iv = (PFNGLMULTITEXCOORD1IVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1iv");
- glMultiTexCoord1s = (PFNGLMULTITEXCOORD1SPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1s");
- glMultiTexCoord1sv = (PFNGLMULTITEXCOORD1SVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1sv");
- glMultiTexCoord2d = (PFNGLMULTITEXCOORD2DPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2d");
- glMultiTexCoord2dv = (PFNGLMULTITEXCOORD2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2dv");
- glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2f");
- glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2fv");
- glMultiTexCoord2i = (PFNGLMULTITEXCOORD2IPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2i");
- glMultiTexCoord2iv = (PFNGLMULTITEXCOORD2IVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2iv");
- glMultiTexCoord2s = (PFNGLMULTITEXCOORD2SPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2s");
- glMultiTexCoord2sv = (PFNGLMULTITEXCOORD2SVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2sv");
- glMultiTexCoord3d = (PFNGLMULTITEXCOORD3DPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3d");
- glMultiTexCoord3dv = (PFNGLMULTITEXCOORD3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3dv");
- glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3f");
- glMultiTexCoord3fv = (PFNGLMULTITEXCOORD3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3fv");
- glMultiTexCoord3i = (PFNGLMULTITEXCOORD3IPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3i");
- glMultiTexCoord3iv = (PFNGLMULTITEXCOORD3IVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3iv");
- glMultiTexCoord3s = (PFNGLMULTITEXCOORD3SPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3s");
- glMultiTexCoord3sv = (PFNGLMULTITEXCOORD3SVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3sv");
- glMultiTexCoord4d = (PFNGLMULTITEXCOORD4DPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4d");
- glMultiTexCoord4dv = (PFNGLMULTITEXCOORD4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4dv");
- glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4f");
- glMultiTexCoord4fv = (PFNGLMULTITEXCOORD4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4fv");
- glMultiTexCoord4i = (PFNGLMULTITEXCOORD4IPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4i");
- glMultiTexCoord4iv = (PFNGLMULTITEXCOORD4IVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4iv");
- glMultiTexCoord4s = (PFNGLMULTITEXCOORD4SPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4s");
- glMultiTexCoord4sv = (PFNGLMULTITEXCOORD4SVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4sv");
- glLoadTransposeMatrixf = (PFNGLLOADTRANSPOSEMATRIXFPROC)GLH_EXT_GET_PROC_ADDRESS("glLoadTransposeMatrixf");
- glLoadTransposeMatrixd = (PFNGLLOADTRANSPOSEMATRIXDPROC)GLH_EXT_GET_PROC_ADDRESS("glLoadTransposeMatrixd");
- glMultTransposeMatrixf = (PFNGLMULTTRANSPOSEMATRIXFPROC)GLH_EXT_GET_PROC_ADDRESS("glMultTransposeMatrixf");
- glMultTransposeMatrixd = (PFNGLMULTTRANSPOSEMATRIXDPROC)GLH_EXT_GET_PROC_ADDRESS("glMultTransposeMatrixd");
-
- // GL_VERSION_1_4
- if (mGLVersion < 1.39f)
- {
- return;
- }
- glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendFuncSeparate");
- glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawArrays");
- glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawElements");
- glPointParameterf = (PFNGLPOINTPARAMETERFPROC)GLH_EXT_GET_PROC_ADDRESS("glPointParameterf");
- glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glPointParameterfv");
- glPointParameteri = (PFNGLPOINTPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glPointParameteri");
- glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glPointParameteriv");
- glFogCoordf = (PFNGLFOGCOORDFPROC)GLH_EXT_GET_PROC_ADDRESS("glFogCoordf");
- glFogCoordfv = (PFNGLFOGCOORDFVPROC)GLH_EXT_GET_PROC_ADDRESS("glFogCoordfv");
- glFogCoordd = (PFNGLFOGCOORDDPROC)GLH_EXT_GET_PROC_ADDRESS("glFogCoordd");
- glFogCoorddv = (PFNGLFOGCOORDDVPROC)GLH_EXT_GET_PROC_ADDRESS("glFogCoorddv");
- glFogCoordPointer = (PFNGLFOGCOORDPOINTERPROC)GLH_EXT_GET_PROC_ADDRESS("glFogCoordPointer");
- glSecondaryColor3b = (PFNGLSECONDARYCOLOR3BPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3b");
- glSecondaryColor3bv = (PFNGLSECONDARYCOLOR3BVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3bv");
- glSecondaryColor3d = (PFNGLSECONDARYCOLOR3DPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3d");
- glSecondaryColor3dv = (PFNGLSECONDARYCOLOR3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3dv");
- glSecondaryColor3f = (PFNGLSECONDARYCOLOR3FPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3f");
- glSecondaryColor3fv = (PFNGLSECONDARYCOLOR3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3fv");
- glSecondaryColor3i = (PFNGLSECONDARYCOLOR3IPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3i");
- glSecondaryColor3iv = (PFNGLSECONDARYCOLOR3IVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3iv");
- glSecondaryColor3s = (PFNGLSECONDARYCOLOR3SPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3s");
- glSecondaryColor3sv = (PFNGLSECONDARYCOLOR3SVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3sv");
- glSecondaryColor3ub = (PFNGLSECONDARYCOLOR3UBPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3ub");
- glSecondaryColor3ubv = (PFNGLSECONDARYCOLOR3UBVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3ubv");
- glSecondaryColor3ui = (PFNGLSECONDARYCOLOR3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3ui");
- glSecondaryColor3uiv = (PFNGLSECONDARYCOLOR3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3uiv");
- glSecondaryColor3us = (PFNGLSECONDARYCOLOR3USPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3us");
- glSecondaryColor3usv = (PFNGLSECONDARYCOLOR3USVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3usv");
- glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColorPointer");
- glWindowPos2d = (PFNGLWINDOWPOS2DPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2d");
- glWindowPos2dv = (PFNGLWINDOWPOS2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2dv");
- glWindowPos2f = (PFNGLWINDOWPOS2FPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2f");
- glWindowPos2fv = (PFNGLWINDOWPOS2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2fv");
- glWindowPos2i = (PFNGLWINDOWPOS2IPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2i");
- glWindowPos2iv = (PFNGLWINDOWPOS2IVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2iv");
- glWindowPos2s = (PFNGLWINDOWPOS2SPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2s");
- glWindowPos2sv = (PFNGLWINDOWPOS2SVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2sv");
- glWindowPos3d = (PFNGLWINDOWPOS3DPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3d");
- glWindowPos3dv = (PFNGLWINDOWPOS3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3dv");
- glWindowPos3f = (PFNGLWINDOWPOS3FPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3f");
- glWindowPos3fv = (PFNGLWINDOWPOS3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3fv");
- glWindowPos3i = (PFNGLWINDOWPOS3IPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3i");
- glWindowPos3iv = (PFNGLWINDOWPOS3IVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3iv");
- glWindowPos3s = (PFNGLWINDOWPOS3SPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3s");
- glWindowPos3sv = (PFNGLWINDOWPOS3SVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3sv");
-
- // GL_VERSION_1_5
- if (mGLVersion < 1.49f)
- {
- return;
- }
- glGenQueries = (PFNGLGENQUERIESPROC)GLH_EXT_GET_PROC_ADDRESS("glGenQueries");
- glDeleteQueries = (PFNGLDELETEQUERIESPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteQueries");
- glIsQuery = (PFNGLISQUERYPROC)GLH_EXT_GET_PROC_ADDRESS("glIsQuery");
- glBeginQuery = (PFNGLBEGINQUERYPROC)GLH_EXT_GET_PROC_ADDRESS("glBeginQuery");
- glEndQuery = (PFNGLENDQUERYPROC)GLH_EXT_GET_PROC_ADDRESS("glEndQuery");
- glGetQueryiv = (PFNGLGETQUERYIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryiv");
- glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryObjectiv");
- glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryObjectuiv");
- glBindBuffer = (PFNGLBINDBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glBindBuffer");
- glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteBuffers");
- glGenBuffers = (PFNGLGENBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glGenBuffers");
- glIsBuffer = (PFNGLISBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glIsBuffer");
- glBufferData = (PFNGLBUFFERDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glBufferData");
- glBufferSubData = (PFNGLBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glBufferSubData");
- glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferSubData");
- glMapBuffer = (PFNGLMAPBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glMapBuffer");
- glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glUnmapBuffer");
- glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferParameteriv");
- glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferPointerv");
-
- // GL_VERSION_2_0
- if (mGLVersion < 1.9f)
- {
- return;
- }
- glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendEquationSeparate");
- glDrawBuffers = (PFNGLDRAWBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawBuffers");
- glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)GLH_EXT_GET_PROC_ADDRESS("glStencilOpSeparate");
- glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)GLH_EXT_GET_PROC_ADDRESS("glStencilFuncSeparate");
- glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)GLH_EXT_GET_PROC_ADDRESS("glStencilMaskSeparate");
- glAttachShader = (PFNGLATTACHSHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glAttachShader");
- glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glBindAttribLocation");
- glCompileShader = (PFNGLCOMPILESHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glCompileShader");
- glCreateProgram = (PFNGLCREATEPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateProgram");
- glCreateShader = (PFNGLCREATESHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateShader");
- glDeleteProgram = (PFNGLDELETEPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteProgram");
- glDeleteShader = (PFNGLDELETESHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteShader");
- glDetachShader = (PFNGLDETACHSHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glDetachShader");
- glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)GLH_EXT_GET_PROC_ADDRESS("glDisableVertexAttribArray");
- glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)GLH_EXT_GET_PROC_ADDRESS("glEnableVertexAttribArray");
- glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveAttrib");
- glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniform");
- glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)GLH_EXT_GET_PROC_ADDRESS("glGetAttachedShaders");
- glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glGetAttribLocation");
- glGetProgramiv = (PFNGLGETPROGRAMIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramiv");
- glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramInfoLog");
- glGetShaderiv = (PFNGLGETSHADERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetShaderiv");
- glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)GLH_EXT_GET_PROC_ADDRESS("glGetShaderInfoLog");
- glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetShaderSource");
- glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformLocation");
- glGetUniformfv = (PFNGLGETUNIFORMFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformfv");
- glGetUniformiv = (PFNGLGETUNIFORMIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformiv");
- glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribdv");
- glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribfv");
- glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribiv");
- glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribPointerv");
- glIsProgram = (PFNGLISPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glIsProgram");
- glIsShader = (PFNGLISSHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glIsShader");
- glLinkProgram = (PFNGLLINKPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glLinkProgram");
- glShaderSource = (PFNGLSHADERSOURCEPROC)GLH_EXT_GET_PROC_ADDRESS("glShaderSource");
- glUseProgram = (PFNGLUSEPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glUseProgram");
- glUniform1f = (PFNGLUNIFORM1FPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1f");
- glUniform2f = (PFNGLUNIFORM2FPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2f");
- glUniform3f = (PFNGLUNIFORM3FPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3f");
- glUniform4f = (PFNGLUNIFORM4FPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4f");
- glUniform1i = (PFNGLUNIFORM1IPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1i");
- glUniform2i = (PFNGLUNIFORM2IPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2i");
- glUniform3i = (PFNGLUNIFORM3IPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3i");
- glUniform4i = (PFNGLUNIFORM4IPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4i");
- glUniform1fv = (PFNGLUNIFORM1FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1fv");
- glUniform2fv = (PFNGLUNIFORM2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2fv");
- glUniform3fv = (PFNGLUNIFORM3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3fv");
- glUniform4fv = (PFNGLUNIFORM4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4fv");
- glUniform1iv = (PFNGLUNIFORM1IVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1iv");
- glUniform2iv = (PFNGLUNIFORM2IVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2iv");
- glUniform3iv = (PFNGLUNIFORM3IVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3iv");
- glUniform4iv = (PFNGLUNIFORM4IVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4iv");
- glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2fv");
- glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3fv");
- glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4fv");
- glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glValidateProgram");
- glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1d");
- glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1dv");
- glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1f");
- glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1fv");
- glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1s");
- glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1sv");
- glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2d");
- glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2dv");
- glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2f");
- glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2fv");
- glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2s");
- glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2sv");
- glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3d");
- glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3dv");
- glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3f");
- glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3fv");
- glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3s");
- glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3sv");
- glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Nbv");
- glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Niv");
- glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Nsv");
- glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Nub");
- glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Nubv");
- glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Nuiv");
- glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Nusv");
- glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4bv");
- glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4d");
- glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4dv");
- glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4f");
- glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4fv");
- glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4iv");
- glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4s");
- glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4sv");
- glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4ubv");
- glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4uiv");
- glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4usv");
- glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribPointer");
-
- // GL_VERSION_2_1
- if (mGLVersion < 2.09f)
- {
- return;
- }
- glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2x3fv");
- glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3x2fv");
- glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2x4fv");
- glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4x2fv");
- glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3x4fv");
- glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4x3fv");
-
- // GL_VERSION_3_0
- if (mGLVersion < 2.99f)
- {
- return;
- }
- glColorMaski = (PFNGLCOLORMASKIPROC)GLH_EXT_GET_PROC_ADDRESS("glColorMaski");
- glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBooleani_v");
- glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetIntegeri_v");
- glEnablei = (PFNGLENABLEIPROC)GLH_EXT_GET_PROC_ADDRESS("glEnablei");
- glDisablei = (PFNGLDISABLEIPROC)GLH_EXT_GET_PROC_ADDRESS("glDisablei");
- glIsEnabledi = (PFNGLISENABLEDIPROC)GLH_EXT_GET_PROC_ADDRESS("glIsEnabledi");
- glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glBeginTransformFeedback");
- glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glEndTransformFeedback");
- glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glBindBufferRange");
- glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)GLH_EXT_GET_PROC_ADDRESS("glBindBufferBase");
- glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)GLH_EXT_GET_PROC_ADDRESS("glTransformFeedbackVaryings");
- glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTransformFeedbackVarying");
- glClampColor = (PFNGLCLAMPCOLORPROC)GLH_EXT_GET_PROC_ADDRESS("glClampColor");
- glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC)GLH_EXT_GET_PROC_ADDRESS("glBeginConditionalRender");
- glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC)GLH_EXT_GET_PROC_ADDRESS("glEndConditionalRender");
- glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribIPointer");
- glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribIiv");
- glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribIuiv");
- glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI1i");
- glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI2i");
- glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI3i");
- glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4i");
- glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI1ui");
- glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI2ui");
- glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI3ui");
- glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4ui");
- glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI1iv");
- glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI2iv");
- glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI3iv");
- glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4iv");
- glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI1uiv");
- glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI2uiv");
- glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI3uiv");
- glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4uiv");
- glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4bv");
- glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4sv");
- glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4ubv");
- glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4usv");
- glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformuiv");
- glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glBindFragDataLocation");
- glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glGetFragDataLocation");
- glUniform1ui = (PFNGLUNIFORM1UIPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1ui");
- glUniform2ui = (PFNGLUNIFORM2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2ui");
- glUniform3ui = (PFNGLUNIFORM3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3ui");
- glUniform4ui = (PFNGLUNIFORM4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4ui");
- glUniform1uiv = (PFNGLUNIFORM1UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1uiv");
- glUniform2uiv = (PFNGLUNIFORM2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2uiv");
- glUniform3uiv = (PFNGLUNIFORM3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3uiv");
- glUniform4uiv = (PFNGLUNIFORM4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4uiv");
- glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTexParameterIiv");
- glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTexParameterIuiv");
- glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTexParameterIiv");
- glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTexParameterIuiv");
- glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferiv");
- glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferuiv");
- glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferfv");
- glClearBufferfi = (PFNGLCLEARBUFFERFIPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferfi");
- glGetStringi = (PFNGLGETSTRINGIPROC)GLH_EXT_GET_PROC_ADDRESS("glGetStringi");
- glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glIsRenderbuffer");
- glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glBindRenderbuffer");
- glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteRenderbuffers");
- glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glGenRenderbuffers");
- glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glRenderbufferStorage");
- glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetRenderbufferParameteriv");
- glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glIsFramebuffer");
- glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glBindFramebuffer");
- glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteFramebuffers");
- glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glGenFramebuffers");
- glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)GLH_EXT_GET_PROC_ADDRESS("glCheckFramebufferStatus");
- glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture1D");
- glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture2D");
- glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture3D");
- glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferRenderbuffer");
- glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetFramebufferAttachmentParameteriv");
- glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)GLH_EXT_GET_PROC_ADDRESS("glGenerateMipmap");
- glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glBlitFramebuffer");
- glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glRenderbufferStorageMultisample");
- glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferTextureLayer");
- glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glMapBufferRange");
- glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glFlushMappedBufferRange");
- glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)GLH_EXT_GET_PROC_ADDRESS("glBindVertexArray");
- glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteVertexArrays");
- glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)GLH_EXT_GET_PROC_ADDRESS("glGenVertexArrays");
- glIsVertexArray = (PFNGLISVERTEXARRAYPROC)GLH_EXT_GET_PROC_ADDRESS("glIsVertexArray");
-
- // GL_VERSION_3_1
- if (mGLVersion < 3.09f)
- {
- return;
- }
- glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawArraysInstanced");
- glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsInstanced");
- glTexBuffer = (PFNGLTEXBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glTexBuffer");
- glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)GLH_EXT_GET_PROC_ADDRESS("glPrimitiveRestartIndex");
- glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyBufferSubData");
- glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformIndices");
- glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniformsiv");
- glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniformName");
- glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformBlockIndex");
- glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniformBlockiv");
- glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniformBlockName");
- glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformBlockBinding");
-
- // GL_VERSION_3_2
- if (mGLVersion < 3.19f)
- {
- return;
- }
- glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsBaseVertex");
- glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawRangeElementsBaseVertex");
- glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsInstancedBaseVertex");
- glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawElementsBaseVertex");
- glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC)GLH_EXT_GET_PROC_ADDRESS("glProvokingVertex");
- glFenceSync = (PFNGLFENCESYNCPROC)GLH_EXT_GET_PROC_ADDRESS("glFenceSync");
- glIsSync = (PFNGLISSYNCPROC)GLH_EXT_GET_PROC_ADDRESS("glIsSync");
- glDeleteSync = (PFNGLDELETESYNCPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteSync");
- glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)GLH_EXT_GET_PROC_ADDRESS("glClientWaitSync");
- glWaitSync = (PFNGLWAITSYNCPROC)GLH_EXT_GET_PROC_ADDRESS("glWaitSync");
- glGetInteger64v = (PFNGLGETINTEGER64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetInteger64v");
- glGetSynciv = (PFNGLGETSYNCIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSynciv");
- glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetInteger64i_v");
- glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferParameteri64v");
- glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture");
- glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glTexImage2DMultisample");
- glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glTexImage3DMultisample");
- glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetMultisamplefv");
- glSampleMaski = (PFNGLSAMPLEMASKIPROC)GLH_EXT_GET_PROC_ADDRESS("glSampleMaski");
-
- // GL_VERSION_3_3
- if (mGLVersion < 3.29f)
- {
- return;
- }
- glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)GLH_EXT_GET_PROC_ADDRESS("glBindFragDataLocationIndexed");
- glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC)GLH_EXT_GET_PROC_ADDRESS("glGetFragDataIndex");
- glGenSamplers = (PFNGLGENSAMPLERSPROC)GLH_EXT_GET_PROC_ADDRESS("glGenSamplers");
- glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteSamplers");
- glIsSampler = (PFNGLISSAMPLERPROC)GLH_EXT_GET_PROC_ADDRESS("glIsSampler");
- glBindSampler = (PFNGLBINDSAMPLERPROC)GLH_EXT_GET_PROC_ADDRESS("glBindSampler");
- glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glSamplerParameteri");
- glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glSamplerParameteriv");
- glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)GLH_EXT_GET_PROC_ADDRESS("glSamplerParameterf");
- glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glSamplerParameterfv");
- glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glSamplerParameterIiv");
- glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glSamplerParameterIuiv");
- glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSamplerParameteriv");
- glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSamplerParameterIiv");
- glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSamplerParameterfv");
- glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSamplerParameterIuiv");
- glQueryCounter = (PFNGLQUERYCOUNTERPROC)GLH_EXT_GET_PROC_ADDRESS("glQueryCounter");
- glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryObjecti64v");
- glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryObjectui64v");
- glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribDivisor");
- glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP1ui");
- glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP1uiv");
- glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP2ui");
- glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP2uiv");
- glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP3ui");
- glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP3uiv");
- glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP4ui");
- glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP4uiv");
- glVertexP2ui = (PFNGLVERTEXP2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexP2ui");
- glVertexP2uiv = (PFNGLVERTEXP2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexP2uiv");
- glVertexP3ui = (PFNGLVERTEXP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexP3ui");
- glVertexP3uiv = (PFNGLVERTEXP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexP3uiv");
- glVertexP4ui = (PFNGLVERTEXP4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexP4ui");
- glVertexP4uiv = (PFNGLVERTEXP4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexP4uiv");
- glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP1ui");
- glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP1uiv");
- glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP2ui");
- glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP2uiv");
- glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP3ui");
- glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP3uiv");
- glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP4ui");
- glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP4uiv");
- glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP1ui");
- glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP1uiv");
- glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP2ui");
- glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP2uiv");
- glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP3ui");
- glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP3uiv");
- glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP4ui");
- glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP4uiv");
- glNormalP3ui = (PFNGLNORMALP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glNormalP3ui");
- glNormalP3uiv = (PFNGLNORMALP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glNormalP3uiv");
- glColorP3ui = (PFNGLCOLORP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glColorP3ui");
- glColorP3uiv = (PFNGLCOLORP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glColorP3uiv");
- glColorP4ui = (PFNGLCOLORP4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glColorP4ui");
- glColorP4uiv = (PFNGLCOLORP4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glColorP4uiv");
- glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColorP3ui");
- glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColorP3uiv");
-
- // GL_VERSION_4_0
- if (mGLVersion < 3.99f)
- {
- return;
- }
- glMinSampleShading = (PFNGLMINSAMPLESHADINGPROC)GLH_EXT_GET_PROC_ADDRESS("glMinSampleShading");
- glBlendEquationi = (PFNGLBLENDEQUATIONIPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendEquationi");
- glBlendEquationSeparatei = (PFNGLBLENDEQUATIONSEPARATEIPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendEquationSeparatei");
- glBlendFunci = (PFNGLBLENDFUNCIPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendFunci");
- glBlendFuncSeparatei = (PFNGLBLENDFUNCSEPARATEIPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendFuncSeparatei");
- glDrawArraysIndirect = (PFNGLDRAWARRAYSINDIRECTPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawArraysIndirect");
- glDrawElementsIndirect = (PFNGLDRAWELEMENTSINDIRECTPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsIndirect");
- glUniform1d = (PFNGLUNIFORM1DPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1d");
- glUniform2d = (PFNGLUNIFORM2DPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2d");
- glUniform3d = (PFNGLUNIFORM3DPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3d");
- glUniform4d = (PFNGLUNIFORM4DPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4d");
- glUniform1dv = (PFNGLUNIFORM1DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1dv");
- glUniform2dv = (PFNGLUNIFORM2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2dv");
- glUniform3dv = (PFNGLUNIFORM3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3dv");
- glUniform4dv = (PFNGLUNIFORM4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4dv");
- glUniformMatrix2dv = (PFNGLUNIFORMMATRIX2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2dv");
- glUniformMatrix3dv = (PFNGLUNIFORMMATRIX3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3dv");
- glUniformMatrix4dv = (PFNGLUNIFORMMATRIX4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4dv");
- glUniformMatrix2x3dv = (PFNGLUNIFORMMATRIX2X3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2x3dv");
- glUniformMatrix2x4dv = (PFNGLUNIFORMMATRIX2X4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2x4dv");
- glUniformMatrix3x2dv = (PFNGLUNIFORMMATRIX3X2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3x2dv");
- glUniformMatrix3x4dv = (PFNGLUNIFORMMATRIX3X4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3x4dv");
- glUniformMatrix4x2dv = (PFNGLUNIFORMMATRIX4X2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4x2dv");
- glUniformMatrix4x3dv = (PFNGLUNIFORMMATRIX4X3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4x3dv");
- glGetUniformdv = (PFNGLGETUNIFORMDVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformdv");
- glGetSubroutineUniformLocation = (PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSubroutineUniformLocation");
- glGetSubroutineIndex = (PFNGLGETSUBROUTINEINDEXPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSubroutineIndex");
- glGetActiveSubroutineUniformiv = (PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveSubroutineUniformiv");
- glGetActiveSubroutineUniformName = (PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveSubroutineUniformName");
- glGetActiveSubroutineName = (PFNGLGETACTIVESUBROUTINENAMEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveSubroutineName");
- glUniformSubroutinesuiv = (PFNGLUNIFORMSUBROUTINESUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformSubroutinesuiv");
- glGetUniformSubroutineuiv = (PFNGLGETUNIFORMSUBROUTINEUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformSubroutineuiv");
- glGetProgramStageiv = (PFNGLGETPROGRAMSTAGEIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramStageiv");
- glPatchParameteri = (PFNGLPATCHPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glPatchParameteri");
- glPatchParameterfv = (PFNGLPATCHPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glPatchParameterfv");
- glBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glBindTransformFeedback");
- glDeleteTransformFeedbacks = (PFNGLDELETETRANSFORMFEEDBACKSPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteTransformFeedbacks");
- glGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC)GLH_EXT_GET_PROC_ADDRESS("glGenTransformFeedbacks");
- glIsTransformFeedback = (PFNGLISTRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glIsTransformFeedback");
- glPauseTransformFeedback = (PFNGLPAUSETRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glPauseTransformFeedback");
- glResumeTransformFeedback = (PFNGLRESUMETRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glResumeTransformFeedback");
- glDrawTransformFeedback = (PFNGLDRAWTRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawTransformFeedback");
- glDrawTransformFeedbackStream = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawTransformFeedbackStream");
- glBeginQueryIndexed = (PFNGLBEGINQUERYINDEXEDPROC)GLH_EXT_GET_PROC_ADDRESS("glBeginQueryIndexed");
- glEndQueryIndexed = (PFNGLENDQUERYINDEXEDPROC)GLH_EXT_GET_PROC_ADDRESS("glEndQueryIndexed");
- glGetQueryIndexediv = (PFNGLGETQUERYINDEXEDIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryIndexediv");
-
- // GL_VERSION_4_1
- if (mGLVersion < 4.09f)
- {
- return;
- }
- glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC)GLH_EXT_GET_PROC_ADDRESS("glReleaseShaderCompiler");
- glShaderBinary = (PFNGLSHADERBINARYPROC)GLH_EXT_GET_PROC_ADDRESS("glShaderBinary");
- glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glGetShaderPrecisionFormat");
- glDepthRangef = (PFNGLDEPTHRANGEFPROC)GLH_EXT_GET_PROC_ADDRESS("glDepthRangef");
- glClearDepthf = (PFNGLCLEARDEPTHFPROC)GLH_EXT_GET_PROC_ADDRESS("glClearDepthf");
- glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramBinary");
- glProgramBinary = (PFNGLPROGRAMBINARYPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramBinary");
- glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramParameteri");
- glUseProgramStages = (PFNGLUSEPROGRAMSTAGESPROC)GLH_EXT_GET_PROC_ADDRESS("glUseProgramStages");
- glActiveShaderProgram = (PFNGLACTIVESHADERPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glActiveShaderProgram");
- glCreateShaderProgramv = (PFNGLCREATESHADERPROGRAMVPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateShaderProgramv");
- glBindProgramPipeline = (PFNGLBINDPROGRAMPIPELINEPROC)GLH_EXT_GET_PROC_ADDRESS("glBindProgramPipeline");
- glDeleteProgramPipelines = (PFNGLDELETEPROGRAMPIPELINESPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteProgramPipelines");
- glGenProgramPipelines = (PFNGLGENPROGRAMPIPELINESPROC)GLH_EXT_GET_PROC_ADDRESS("glGenProgramPipelines");
- glIsProgramPipeline = (PFNGLISPROGRAMPIPELINEPROC)GLH_EXT_GET_PROC_ADDRESS("glIsProgramPipeline");
- glGetProgramPipelineiv = (PFNGLGETPROGRAMPIPELINEIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramPipelineiv");
- glProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1i");
- glProgramUniform1iv = (PFNGLPROGRAMUNIFORM1IVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1iv");
- glProgramUniform1f = (PFNGLPROGRAMUNIFORM1FPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1f");
- glProgramUniform1fv = (PFNGLPROGRAMUNIFORM1FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1fv");
- glProgramUniform1d = (PFNGLPROGRAMUNIFORM1DPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1d");
- glProgramUniform1dv = (PFNGLPROGRAMUNIFORM1DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1dv");
- glProgramUniform1ui = (PFNGLPROGRAMUNIFORM1UIPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1ui");
- glProgramUniform1uiv = (PFNGLPROGRAMUNIFORM1UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1uiv");
- glProgramUniform2i = (PFNGLPROGRAMUNIFORM2IPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2i");
- glProgramUniform2iv = (PFNGLPROGRAMUNIFORM2IVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2iv");
- glProgramUniform2f = (PFNGLPROGRAMUNIFORM2FPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2f");
- glProgramUniform2fv = (PFNGLPROGRAMUNIFORM2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2fv");
- glProgramUniform2d = (PFNGLPROGRAMUNIFORM2DPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2d");
- glProgramUniform2dv = (PFNGLPROGRAMUNIFORM2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2dv");
- glProgramUniform2ui = (PFNGLPROGRAMUNIFORM2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2ui");
- glProgramUniform2uiv = (PFNGLPROGRAMUNIFORM2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2uiv");
- glProgramUniform3i = (PFNGLPROGRAMUNIFORM3IPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3i");
- glProgramUniform3iv = (PFNGLPROGRAMUNIFORM3IVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3iv");
- glProgramUniform3f = (PFNGLPROGRAMUNIFORM3FPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3f");
- glProgramUniform3fv = (PFNGLPROGRAMUNIFORM3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3fv");
- glProgramUniform3d = (PFNGLPROGRAMUNIFORM3DPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3d");
- glProgramUniform3dv = (PFNGLPROGRAMUNIFORM3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3dv");
- glProgramUniform3ui = (PFNGLPROGRAMUNIFORM3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3ui");
- glProgramUniform3uiv = (PFNGLPROGRAMUNIFORM3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3uiv");
- glProgramUniform4i = (PFNGLPROGRAMUNIFORM4IPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4i");
- glProgramUniform4iv = (PFNGLPROGRAMUNIFORM4IVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4iv");
- glProgramUniform4f = (PFNGLPROGRAMUNIFORM4FPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4f");
- glProgramUniform4fv = (PFNGLPROGRAMUNIFORM4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4fv");
- glProgramUniform4d = (PFNGLPROGRAMUNIFORM4DPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4d");
- glProgramUniform4dv = (PFNGLPROGRAMUNIFORM4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4dv");
- glProgramUniform4ui = (PFNGLPROGRAMUNIFORM4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4ui");
- glProgramUniform4uiv = (PFNGLPROGRAMUNIFORM4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4uiv");
- glProgramUniformMatrix2fv = (PFNGLPROGRAMUNIFORMMATRIX2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix2fv");
- glProgramUniformMatrix3fv = (PFNGLPROGRAMUNIFORMMATRIX3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix3fv");
- glProgramUniformMatrix4fv = (PFNGLPROGRAMUNIFORMMATRIX4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix4fv");
- glProgramUniformMatrix2dv = (PFNGLPROGRAMUNIFORMMATRIX2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix2dv");
- glProgramUniformMatrix3dv = (PFNGLPROGRAMUNIFORMMATRIX3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix3dv");
- glProgramUniformMatrix4dv = (PFNGLPROGRAMUNIFORMMATRIX4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix4dv");
- glProgramUniformMatrix2x3fv = (PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix2x3fv");
- glProgramUniformMatrix3x2fv = (PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix3x2fv");
- glProgramUniformMatrix2x4fv = (PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix2x4fv");
- glProgramUniformMatrix4x2fv = (PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix4x2fv");
- glProgramUniformMatrix3x4fv = (PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix3x4fv");
- glProgramUniformMatrix4x3fv = (PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix4x3fv");
- glProgramUniformMatrix2x3dv = (PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix2x3dv");
- glProgramUniformMatrix3x2dv = (PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix3x2dv");
- glProgramUniformMatrix2x4dv = (PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix2x4dv");
- glProgramUniformMatrix4x2dv = (PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix4x2dv");
- glProgramUniformMatrix3x4dv = (PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix3x4dv");
- glProgramUniformMatrix4x3dv = (PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix4x3dv");
- glValidateProgramPipeline = (PFNGLVALIDATEPROGRAMPIPELINEPROC)GLH_EXT_GET_PROC_ADDRESS("glValidateProgramPipeline");
- glGetProgramPipelineInfoLog = (PFNGLGETPROGRAMPIPELINEINFOLOGPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramPipelineInfoLog");
- glVertexAttribL1d = (PFNGLVERTEXATTRIBL1DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL1d");
- glVertexAttribL2d = (PFNGLVERTEXATTRIBL2DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL2d");
- glVertexAttribL3d = (PFNGLVERTEXATTRIBL3DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL3d");
- glVertexAttribL4d = (PFNGLVERTEXATTRIBL4DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL4d");
- glVertexAttribL1dv = (PFNGLVERTEXATTRIBL1DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL1dv");
- glVertexAttribL2dv = (PFNGLVERTEXATTRIBL2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL2dv");
- glVertexAttribL3dv = (PFNGLVERTEXATTRIBL3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL3dv");
- glVertexAttribL4dv = (PFNGLVERTEXATTRIBL4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL4dv");
- glVertexAttribLPointer = (PFNGLVERTEXATTRIBLPOINTERPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribLPointer");
- glGetVertexAttribLdv = (PFNGLGETVERTEXATTRIBLDVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribLdv");
- glViewportArrayv = (PFNGLVIEWPORTARRAYVPROC)GLH_EXT_GET_PROC_ADDRESS("glViewportArrayv");
- glViewportIndexedf = (PFNGLVIEWPORTINDEXEDFPROC)GLH_EXT_GET_PROC_ADDRESS("glViewportIndexedf");
- glViewportIndexedfv = (PFNGLVIEWPORTINDEXEDFVPROC)GLH_EXT_GET_PROC_ADDRESS("glViewportIndexedfv");
- glScissorArrayv = (PFNGLSCISSORARRAYVPROC)GLH_EXT_GET_PROC_ADDRESS("glScissorArrayv");
- glScissorIndexed = (PFNGLSCISSORINDEXEDPROC)GLH_EXT_GET_PROC_ADDRESS("glScissorIndexed");
- glScissorIndexedv = (PFNGLSCISSORINDEXEDVPROC)GLH_EXT_GET_PROC_ADDRESS("glScissorIndexedv");
- glDepthRangeArrayv = (PFNGLDEPTHRANGEARRAYVPROC)GLH_EXT_GET_PROC_ADDRESS("glDepthRangeArrayv");
- glDepthRangeIndexed = (PFNGLDEPTHRANGEINDEXEDPROC)GLH_EXT_GET_PROC_ADDRESS("glDepthRangeIndexed");
- glGetFloati_v = (PFNGLGETFLOATI_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetFloati_v");
- glGetDoublei_v = (PFNGLGETDOUBLEI_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetDoublei_v");
-
- // GL_VERSION_4_2
- if (mGLVersion < 4.19f)
- {
- return;
- }
- glDrawArraysInstancedBaseInstance = (PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawArraysInstancedBaseInstance");
- glDrawElementsInstancedBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsInstancedBaseInstance");
- glDrawElementsInstancedBaseVertexBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsInstancedBaseVertexBaseInstance");
- glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetInternalformativ");
- glGetActiveAtomicCounterBufferiv = (PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveAtomicCounterBufferiv");
- glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC)GLH_EXT_GET_PROC_ADDRESS("glBindImageTexture");
- glMemoryBarrier = (PFNGLMEMORYBARRIERPROC)GLH_EXT_GET_PROC_ADDRESS("glMemoryBarrier");
- glTexStorage1D = (PFNGLTEXSTORAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glTexStorage1D");
- glTexStorage2D = (PFNGLTEXSTORAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glTexStorage2D");
- glTexStorage3D = (PFNGLTEXSTORAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glTexStorage3D");
- glDrawTransformFeedbackInstanced = (PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawTransformFeedbackInstanced");
- glDrawTransformFeedbackStreamInstanced = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawTransformFeedbackStreamInstanced");
-
- // GL_VERSION_4_3
- if (mGLVersion < 4.29f)
- {
- return;
- }
- glClearBufferData = (PFNGLCLEARBUFFERDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferData");
- glClearBufferSubData = (PFNGLCLEARBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferSubData");
- glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC)GLH_EXT_GET_PROC_ADDRESS("glDispatchCompute");
- glDispatchComputeIndirect = (PFNGLDISPATCHCOMPUTEINDIRECTPROC)GLH_EXT_GET_PROC_ADDRESS("glDispatchComputeIndirect");
- glCopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyImageSubData");
- glFramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferParameteri");
- glGetFramebufferParameteriv = (PFNGLGETFRAMEBUFFERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetFramebufferParameteriv");
- glGetInternalformati64v = (PFNGLGETINTERNALFORMATI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetInternalformati64v");
- glInvalidateTexSubImage = (PFNGLINVALIDATETEXSUBIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateTexSubImage");
- glInvalidateTexImage = (PFNGLINVALIDATETEXIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateTexImage");
- glInvalidateBufferSubData = (PFNGLINVALIDATEBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateBufferSubData");
- glInvalidateBufferData = (PFNGLINVALIDATEBUFFERDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateBufferData");
- glInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateFramebuffer");
- glInvalidateSubFramebuffer = (PFNGLINVALIDATESUBFRAMEBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateSubFramebuffer");
- glMultiDrawArraysIndirect = (PFNGLMULTIDRAWARRAYSINDIRECTPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawArraysIndirect");
- glMultiDrawElementsIndirect = (PFNGLMULTIDRAWELEMENTSINDIRECTPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawElementsIndirect");
- glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramInterfaceiv");
- glGetProgramResourceIndex = (PFNGLGETPROGRAMRESOURCEINDEXPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramResourceIndex");
- glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramResourceName");
- glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramResourceiv");
- glGetProgramResourceLocation = (PFNGLGETPROGRAMRESOURCELOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramResourceLocation");
- glGetProgramResourceLocationIndex = (PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramResourceLocationIndex");
- glShaderStorageBlockBinding = (PFNGLSHADERSTORAGEBLOCKBINDINGPROC)GLH_EXT_GET_PROC_ADDRESS("glShaderStorageBlockBinding");
- glTexBufferRange = (PFNGLTEXBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glTexBufferRange");
- glTexStorage2DMultisample = (PFNGLTEXSTORAGE2DMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glTexStorage2DMultisample");
- glTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glTexStorage3DMultisample");
- glTextureView = (PFNGLTEXTUREVIEWPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureView");
- glBindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glBindVertexBuffer");
- glVertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribFormat");
- glVertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribIFormat");
- glVertexAttribLFormat = (PFNGLVERTEXATTRIBLFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribLFormat");
- glVertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribBinding");
- glVertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexBindingDivisor");
- glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)GLH_EXT_GET_PROC_ADDRESS("glDebugMessageControl");
- glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC)GLH_EXT_GET_PROC_ADDRESS("glDebugMessageInsert");
- glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glDebugMessageCallback");
- glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC)GLH_EXT_GET_PROC_ADDRESS("glGetDebugMessageLog");
- glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC)GLH_EXT_GET_PROC_ADDRESS("glPushDebugGroup");
- glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC)GLH_EXT_GET_PROC_ADDRESS("glPopDebugGroup");
- glObjectLabel = (PFNGLOBJECTLABELPROC)GLH_EXT_GET_PROC_ADDRESS("glObjectLabel");
- glGetObjectLabel = (PFNGLGETOBJECTLABELPROC)GLH_EXT_GET_PROC_ADDRESS("glGetObjectLabel");
- glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC)GLH_EXT_GET_PROC_ADDRESS("glObjectPtrLabel");
- glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)GLH_EXT_GET_PROC_ADDRESS("glGetObjectPtrLabel");
-
- // GL_VERSION_4_4
- if (mGLVersion < 4.39f)
- {
- return;
- }
- glBufferStorage = (PFNGLBUFFERSTORAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glBufferStorage");
- glClearTexImage = (PFNGLCLEARTEXIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glClearTexImage");
- glClearTexSubImage = (PFNGLCLEARTEXSUBIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glClearTexSubImage");
- glBindBuffersBase = (PFNGLBINDBUFFERSBASEPROC)GLH_EXT_GET_PROC_ADDRESS("glBindBuffersBase");
- glBindBuffersRange = (PFNGLBINDBUFFERSRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glBindBuffersRange");
- glBindTextures = (PFNGLBINDTEXTURESPROC)GLH_EXT_GET_PROC_ADDRESS("glBindTextures");
- glBindSamplers = (PFNGLBINDSAMPLERSPROC)GLH_EXT_GET_PROC_ADDRESS("glBindSamplers");
- glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC)GLH_EXT_GET_PROC_ADDRESS("glBindImageTextures");
- glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glBindVertexBuffers");
-
- // GL_VERSION_4_5
- if (mGLVersion < 4.49f)
- {
- return;
- }
- glClipControl = (PFNGLCLIPCONTROLPROC)GLH_EXT_GET_PROC_ADDRESS("glClipControl");
- glCreateTransformFeedbacks = (PFNGLCREATETRANSFORMFEEDBACKSPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateTransformFeedbacks");
- glTransformFeedbackBufferBase = (PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC)GLH_EXT_GET_PROC_ADDRESS("glTransformFeedbackBufferBase");
- glTransformFeedbackBufferRange = (PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glTransformFeedbackBufferRange");
- glGetTransformFeedbackiv = (PFNGLGETTRANSFORMFEEDBACKIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTransformFeedbackiv");
- glGetTransformFeedbacki_v = (PFNGLGETTRANSFORMFEEDBACKI_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTransformFeedbacki_v");
- glGetTransformFeedbacki64_v = (PFNGLGETTRANSFORMFEEDBACKI64_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTransformFeedbacki64_v");
- glCreateBuffers = (PFNGLCREATEBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateBuffers");
- glNamedBufferStorage = (PFNGLNAMEDBUFFERSTORAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedBufferStorage");
- glNamedBufferData = (PFNGLNAMEDBUFFERDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedBufferData");
- glNamedBufferSubData = (PFNGLNAMEDBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedBufferSubData");
- glCopyNamedBufferSubData = (PFNGLCOPYNAMEDBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyNamedBufferSubData");
- glClearNamedBufferData = (PFNGLCLEARNAMEDBUFFERDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glClearNamedBufferData");
- glClearNamedBufferSubData = (PFNGLCLEARNAMEDBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glClearNamedBufferSubData");
- glMapNamedBuffer = (PFNGLMAPNAMEDBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glMapNamedBuffer");
- glMapNamedBufferRange = (PFNGLMAPNAMEDBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glMapNamedBufferRange");
- glUnmapNamedBuffer = (PFNGLUNMAPNAMEDBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glUnmapNamedBuffer");
- glFlushMappedNamedBufferRange = (PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glFlushMappedNamedBufferRange");
- glGetNamedBufferParameteriv = (PFNGLGETNAMEDBUFFERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedBufferParameteriv");
- glGetNamedBufferParameteri64v = (PFNGLGETNAMEDBUFFERPARAMETERI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedBufferParameteri64v");
- glGetNamedBufferPointerv = (PFNGLGETNAMEDBUFFERPOINTERVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedBufferPointerv");
- glGetNamedBufferSubData = (PFNGLGETNAMEDBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedBufferSubData");
- glCreateFramebuffers = (PFNGLCREATEFRAMEBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateFramebuffers");
- glNamedFramebufferRenderbuffer = (PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferRenderbuffer");
- glNamedFramebufferParameteri = (PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferParameteri");
- glNamedFramebufferTexture = (PFNGLNAMEDFRAMEBUFFERTEXTUREPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferTexture");
- glNamedFramebufferTextureLayer = (PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferTextureLayer");
- glNamedFramebufferDrawBuffer = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferDrawBuffer");
- glNamedFramebufferDrawBuffers = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferDrawBuffers");
- glNamedFramebufferReadBuffer = (PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferReadBuffer");
- glInvalidateNamedFramebufferData = (PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateNamedFramebufferData");
- glInvalidateNamedFramebufferSubData = (PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateNamedFramebufferSubData");
- glClearNamedFramebufferiv = (PFNGLCLEARNAMEDFRAMEBUFFERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glClearNamedFramebufferiv");
- glClearNamedFramebufferuiv = (PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glClearNamedFramebufferuiv");
- glClearNamedFramebufferfv = (PFNGLCLEARNAMEDFRAMEBUFFERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glClearNamedFramebufferfv");
- glClearNamedFramebufferfi = (PFNGLCLEARNAMEDFRAMEBUFFERFIPROC)GLH_EXT_GET_PROC_ADDRESS("glClearNamedFramebufferfi");
- glBlitNamedFramebuffer = (PFNGLBLITNAMEDFRAMEBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glBlitNamedFramebuffer");
- glCheckNamedFramebufferStatus = (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)GLH_EXT_GET_PROC_ADDRESS("glCheckNamedFramebufferStatus");
- glGetNamedFramebufferParameteriv = (PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedFramebufferParameteriv");
- glGetNamedFramebufferAttachmentParameteriv = (PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedFramebufferAttachmentParameteriv");
- glCreateRenderbuffers = (PFNGLCREATERENDERBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateRenderbuffers");
- glNamedRenderbufferStorage = (PFNGLNAMEDRENDERBUFFERSTORAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedRenderbufferStorage");
- glNamedRenderbufferStorageMultisample = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedRenderbufferStorageMultisample");
- glGetNamedRenderbufferParameteriv = (PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedRenderbufferParameteriv");
- glCreateTextures = (PFNGLCREATETEXTURESPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateTextures");
- glTextureBuffer = (PFNGLTEXTUREBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureBuffer");
- glTextureBufferRange = (PFNGLTEXTUREBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureBufferRange");
- glTextureStorage1D = (PFNGLTEXTURESTORAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureStorage1D");
- glTextureStorage2D = (PFNGLTEXTURESTORAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureStorage2D");
- glTextureStorage3D = (PFNGLTEXTURESTORAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureStorage3D");
- glTextureStorage2DMultisample = (PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureStorage2DMultisample");
- glTextureStorage3DMultisample = (PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureStorage3DMultisample");
- glTextureSubImage1D = (PFNGLTEXTURESUBIMAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureSubImage1D");
- glTextureSubImage2D = (PFNGLTEXTURESUBIMAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureSubImage2D");
- glTextureSubImage3D = (PFNGLTEXTURESUBIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureSubImage3D");
- glCompressedTextureSubImage1D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTextureSubImage1D");
- glCompressedTextureSubImage2D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTextureSubImage2D");
- glCompressedTextureSubImage3D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTextureSubImage3D");
- glCopyTextureSubImage1D = (PFNGLCOPYTEXTURESUBIMAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyTextureSubImage1D");
- glCopyTextureSubImage2D = (PFNGLCOPYTEXTURESUBIMAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyTextureSubImage2D");
- glCopyTextureSubImage3D = (PFNGLCOPYTEXTURESUBIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyTextureSubImage3D");
- glTextureParameterf = (PFNGLTEXTUREPARAMETERFPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureParameterf");
- glTextureParameterfv = (PFNGLTEXTUREPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureParameterfv");
- glTextureParameteri = (PFNGLTEXTUREPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureParameteri");
- glTextureParameterIiv = (PFNGLTEXTUREPARAMETERIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureParameterIiv");
- glTextureParameterIuiv = (PFNGLTEXTUREPARAMETERIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureParameterIuiv");
- glTextureParameteriv = (PFNGLTEXTUREPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureParameteriv");
- glGenerateTextureMipmap = (PFNGLGENERATETEXTUREMIPMAPPROC)GLH_EXT_GET_PROC_ADDRESS("glGenerateTextureMipmap");
- glBindTextureUnit = (PFNGLBINDTEXTUREUNITPROC)GLH_EXT_GET_PROC_ADDRESS("glBindTextureUnit");
- glGetTextureImage = (PFNGLGETTEXTUREIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureImage");
- glGetCompressedTextureImage = (PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetCompressedTextureImage");
- glGetTextureLevelParameterfv = (PFNGLGETTEXTURELEVELPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureLevelParameterfv");
- glGetTextureLevelParameteriv = (PFNGLGETTEXTURELEVELPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureLevelParameteriv");
- glGetTextureParameterfv = (PFNGLGETTEXTUREPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureParameterfv");
- glGetTextureParameterIiv = (PFNGLGETTEXTUREPARAMETERIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureParameterIiv");
- glGetTextureParameterIuiv = (PFNGLGETTEXTUREPARAMETERIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureParameterIuiv");
- glGetTextureParameteriv = (PFNGLGETTEXTUREPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureParameteriv");
- glCreateVertexArrays = (PFNGLCREATEVERTEXARRAYSPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateVertexArrays");
- glDisableVertexArrayAttrib = (PFNGLDISABLEVERTEXARRAYATTRIBPROC)GLH_EXT_GET_PROC_ADDRESS("glDisableVertexArrayAttrib");
- glEnableVertexArrayAttrib = (PFNGLENABLEVERTEXARRAYATTRIBPROC)GLH_EXT_GET_PROC_ADDRESS("glEnableVertexArrayAttrib");
- glVertexArrayElementBuffer = (PFNGLVERTEXARRAYELEMENTBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayElementBuffer");
- glVertexArrayVertexBuffer = (PFNGLVERTEXARRAYVERTEXBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayVertexBuffer");
- glVertexArrayVertexBuffers = (PFNGLVERTEXARRAYVERTEXBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayVertexBuffers");
- glVertexArrayAttribBinding = (PFNGLVERTEXARRAYATTRIBBINDINGPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayAttribBinding");
- glVertexArrayAttribFormat = (PFNGLVERTEXARRAYATTRIBFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayAttribFormat");
- glVertexArrayAttribIFormat = (PFNGLVERTEXARRAYATTRIBIFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayAttribIFormat");
- glVertexArrayAttribLFormat = (PFNGLVERTEXARRAYATTRIBLFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayAttribLFormat");
- glVertexArrayBindingDivisor = (PFNGLVERTEXARRAYBINDINGDIVISORPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayBindingDivisor");
- glGetVertexArrayiv = (PFNGLGETVERTEXARRAYIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexArrayiv");
- glGetVertexArrayIndexediv = (PFNGLGETVERTEXARRAYINDEXEDIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexArrayIndexediv");
- glGetVertexArrayIndexed64iv = (PFNGLGETVERTEXARRAYINDEXED64IVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexArrayIndexed64iv");
- glCreateSamplers = (PFNGLCREATESAMPLERSPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateSamplers");
- glCreateProgramPipelines = (PFNGLCREATEPROGRAMPIPELINESPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateProgramPipelines");
- glCreateQueries = (PFNGLCREATEQUERIESPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateQueries");
- glGetQueryBufferObjecti64v = (PFNGLGETQUERYBUFFEROBJECTI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryBufferObjecti64v");
- glGetQueryBufferObjectiv = (PFNGLGETQUERYBUFFEROBJECTIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryBufferObjectiv");
- glGetQueryBufferObjectui64v = (PFNGLGETQUERYBUFFEROBJECTUI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryBufferObjectui64v");
- glGetQueryBufferObjectuiv = (PFNGLGETQUERYBUFFEROBJECTUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryBufferObjectuiv");
- glMemoryBarrierByRegion = (PFNGLMEMORYBARRIERBYREGIONPROC)GLH_EXT_GET_PROC_ADDRESS("glMemoryBarrierByRegion");
- glGetTextureSubImage = (PFNGLGETTEXTURESUBIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureSubImage");
- glGetCompressedTextureSubImage = (PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetCompressedTextureSubImage");
- glGetGraphicsResetStatus = (PFNGLGETGRAPHICSRESETSTATUSPROC)GLH_EXT_GET_PROC_ADDRESS("glGetGraphicsResetStatus");
- glGetnCompressedTexImage = (PFNGLGETNCOMPRESSEDTEXIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnCompressedTexImage");
- glGetnTexImage = (PFNGLGETNTEXIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnTexImage");
- glGetnUniformdv = (PFNGLGETNUNIFORMDVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnUniformdv");
- glGetnUniformfv = (PFNGLGETNUNIFORMFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnUniformfv");
- glGetnUniformiv = (PFNGLGETNUNIFORMIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnUniformiv");
- glGetnUniformuiv = (PFNGLGETNUNIFORMUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnUniformuiv");
- glReadnPixels = (PFNGLREADNPIXELSPROC)GLH_EXT_GET_PROC_ADDRESS("glReadnPixels");
- glGetnMapdv = (PFNGLGETNMAPDVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnMapdv");
- glGetnMapfv = (PFNGLGETNMAPFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnMapfv");
- glGetnMapiv = (PFNGLGETNMAPIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnMapiv");
- glGetnPixelMapfv = (PFNGLGETNPIXELMAPFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnPixelMapfv");
- glGetnPixelMapuiv = (PFNGLGETNPIXELMAPUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnPixelMapuiv");
- glGetnPixelMapusv = (PFNGLGETNPIXELMAPUSVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnPixelMapusv");
- glGetnPolygonStipple = (PFNGLGETNPOLYGONSTIPPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnPolygonStipple");
- glGetnColorTable = (PFNGLGETNCOLORTABLEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnColorTable");
- glGetnConvolutionFilter = (PFNGLGETNCONVOLUTIONFILTERPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnConvolutionFilter");
- glGetnSeparableFilter = (PFNGLGETNSEPARABLEFILTERPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnSeparableFilter");
- glGetnHistogram = (PFNGLGETNHISTOGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnHistogram");
- glGetnMinmax = (PFNGLGETNMINMAXPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnMinmax");
- glTextureBarrier = (PFNGLTEXTUREBARRIERPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureBarrier");
-
- // GL_VERSION_4_6
- if (mGLVersion < 4.59f)
- {
- return;
- }
- glSpecializeShader = (PFNGLSPECIALIZESHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glSpecializeShader");
- glMultiDrawArraysIndirectCount = (PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawArraysIndirectCount");
- glMultiDrawElementsIndirectCount = (PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawElementsIndirectCount");
- glPolygonOffsetClamp = (PFNGLPOLYGONOFFSETCLAMPPROC)GLH_EXT_GET_PROC_ADDRESS("glPolygonOffsetClamp");
-
-#endif
-}
-
-void rotate_quat(LLQuaternion& rotation)
-{
- F32 angle_radians, x, y, z;
- rotation.getAngleAxis(&angle_radians, &x, &y, &z);
- gGL.rotatef(angle_radians * RAD_TO_DEG, x, y, z);
-}
-
-void flush_glerror()
-{
- glGetError();
-}
-
-//this function outputs gl error to the log file, does not crash the code.
-void log_glerror()
-{
- if (LL_UNLIKELY(!gGLManager.mInited))
- {
- return ;
- }
- // Create or update texture to be used with this data
- GLenum error;
- error = glGetError();
- while (LL_UNLIKELY(error))
- {
- GLubyte const * gl_error_msg = gluErrorString(error);
- if (NULL != gl_error_msg)
- {
- LL_WARNS() << "GL Error: " << error << " GL Error String: " << gl_error_msg << LL_ENDL ;
- }
- else
- {
- // gluErrorString returns NULL for some extensions' error codes.
- // you'll probably have to grep for the number in glext.h.
- LL_WARNS() << "GL Error: UNKNOWN 0x" << std::hex << error << std::dec << LL_ENDL;
- }
- error = glGetError();
- }
-}
-
-void do_assert_glerror()
-{
- // Create or update texture to be used with this data
- GLenum error;
- error = glGetError();
- bool quit = false;
- if (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;
- }
- }
- }
-
- if (quit)
- {
- if (gDebugSession)
- {
- ll_fail("assert_glerror failed");
- }
- else
- {
- LL_ERRS() << "One or more unhandled GL errors." << LL_ENDL;
- }
- }
-}
-
-void assert_glerror()
-{
-/* if (!gGLActive)
- {
- //LL_WARNS() << "GL used while not active!" << LL_ENDL;
-
- if (gDebugSession)
- {
- //ll_fail("GL used while not active");
- }
- }
-*/
-
- if (!gDebugGL)
- {
- //funny looking if for branch prediction -- gDebugGL is almost always false and assert_glerror is called often
- }
- else
- {
- do_assert_glerror();
- }
-}
-
-
-void clear_glerror()
-{
- glGetError();
- 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] = GL_FALSE;
- glDisable(GL_MULTISAMPLE);
-}
-
-//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();
- glClientActiveTexture(GL_TEXTURE0+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(GLboolean writeAlpha)
-{
- if (!gDebugGL)
- {
- return;
- }
-
- GLint src;
- GLint dst;
- glGetIntegerv(GL_BLEND_SRC, &src);
- glGetIntegerv(GL_BLEND_DST, &dst);
- llassert_always(src == GL_SRC_ALPHA);
- llassert_always(dst == GL_ONE_MINUS_SRC_ALPHA);
-
- // disable for now until usage is consistent
- //GLboolean colorMask[4];
- //glGetBooleanv(GL_COLOR_WRITEMASK, colorMask);
- //llassert_always(colorMask[0]);
- //llassert_always(colorMask[1]);
- //llassert_always(colorMask[2]);
- // llassert_always(colorMask[3] == writeAlpha);
-
- 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();
- LL_GL_ERRS << llformat("LLGLState error. State: 0x%04x",state) << LL_ENDL;
- }
- }
-}
-
-///////////////////////////////////////////////////////////////////////
-
-LLGLState::LLGLState(LLGLenum state, S32 enabled) :
- mState(state), mWasEnabled(false), mIsEnabled(false)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE;
-
- if (mState)
- {
- mWasEnabled = sStateMap[state];
- setEnabled(enabled);
- }
-}
-
-void LLGLState::setEnabled(S32 enabled)
-{
- if (!mState)
- {
- return;
- }
- if (enabled == CURRENT_STATE)
- {
- enabled = sStateMap[mState] == GL_TRUE ? ENABLED_STATE : DISABLED_STATE;
- }
- else if (enabled == ENABLED_STATE && sStateMap[mState] != GL_TRUE)
- {
- gGL.flush();
- glEnable(mState);
- sStateMap[mState] = GL_TRUE;
- }
- else if (enabled == DISABLED_STATE && sStateMap[mState] != GL_FALSE)
- {
- gGL.flush();
- glDisable(mState);
- sStateMap[mState] = GL_FALSE;
- }
- mIsEnabled = enabled;
-}
-
-LLGLState::~LLGLState()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE;
- 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;
- }
- }
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-void LLGLManager::initGLStates()
-{
- //gl states moved to classes in llglstates.h
- LLGLState::initClass();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific, std::string* version_string )
-{
- // 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;
- }
-
- version_string->assign(version);
-
- 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 );
- }
-}
-
-
-void parse_glsl_version(S32& major, S32& minor)
-{
- // GL_SHADING_LANGUAGE_VERSION returns a null-terminated string with the format:
- // <major>.<minor>[.<release>] [<vendor specific>]
-
- const char* version = (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION);
- major = 0;
- minor = 0;
-
- 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);
-}
-
-LLGLUserClipPlane::LLGLUserClipPlane(const LLPlane& p, const glh::matrix4f& modelview, const glh::matrix4f& projection, bool apply)
-{
- mApply = apply;
-
- if (mApply)
- {
- mModelview = modelview;
- mProjection = projection;
-
- //flip incoming LLPlane to get consistent behavior compared to frustum culling
- setPlane(-p[0], -p[1], -p[2], -p[3]);
- }
-}
-
-void LLGLUserClipPlane::disable()
-{
- if (mApply)
- {
- gGL.matrixMode(LLRender::MM_PROJECTION);
- gGL.popMatrix();
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- }
- mApply = false;
-}
-
-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;
- gGL.matrixMode(LLRender::MM_PROJECTION);
- gGL.pushMatrix();
- gGL.loadMatrix(newP.m);
- gGLObliqueProjectionInverse = LLMatrix4(newP.inverse().transpose().m);
- gGL.matrixMode(LLRender::MM_MODELVIEW);
-}
-
-LLGLUserClipPlane::~LLGLUserClipPlane()
-{
- disable();
-}
-
-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 = GL_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 = GL_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;
- }
- }
- }
-}
-
-LLGLSquashToFarClip::LLGLSquashToFarClip()
-{
- glh::matrix4f proj = get_current_projection();
- setProjectionMatrix(proj, 0);
-}
-
-LLGLSquashToFarClip::LLGLSquashToFarClip(glh::matrix4f& P, U32 layer)
-{
- setProjectionMatrix(P, layer);
-}
-
-
-void LLGLSquashToFarClip::setProjectionMatrix(glh::matrix4f& projection, U32 layer)
-{
-
- F32 depth = 0.99999f - 0.0001f * layer;
-
- for (U32 i = 0; i < 4; i++)
- {
- projection.element(2, i) = projection.element(3, i) * depth;
- }
-
- LLRender::eMatrixMode last_matrix_mode = gGL.getMatrixMode();
-
- gGL.matrixMode(LLRender::MM_PROJECTION);
- gGL.pushMatrix();
- gGL.loadMatrix(projection.m);
-
- gGL.matrixMode(last_matrix_mode);
-}
-
-LLGLSquashToFarClip::~LLGLSquashToFarClip()
-{
- LLRender::eMatrixMode last_matrix_mode = gGL.getMatrixMode();
-
- gGL.matrixMode(LLRender::MM_PROJECTION);
- gGL.popMatrix();
-
- gGL.matrixMode(last_matrix_mode);
-}
-
-
-
-LLGLSyncFence::LLGLSyncFence()
-{
- mSync = 0;
-}
-
-LLGLSyncFence::~LLGLSyncFence()
-{
- if (mSync)
- {
- glDeleteSync(mSync);
- }
-}
-
-void LLGLSyncFence::placeFence()
-{
- if (mSync)
- {
- glDeleteSync(mSync);
- }
- mSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
-}
-
-bool LLGLSyncFence::isCompleted()
-{
- bool ret = true;
- if (mSync)
- {
- GLenum status = glClientWaitSync(mSync, 0, 1);
- if (status == GL_TIMEOUT_EXPIRED)
- {
- ret = false;
- }
- }
- return ret;
-}
-
-void LLGLSyncFence::wait()
-{
- if (mSync)
- {
- while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED)
- { //track the number of times we've waited here
- }
- }
-}
-
-LLGLSPipelineSkyBox::LLGLSPipelineSkyBox()
-: mCullFace(GL_CULL_FACE)
-, mSquashClip()
-{
-}
-
-LLGLSPipelineSkyBox::~LLGLSPipelineSkyBox()
-{
-}
-
-LLGLSPipelineDepthTestSkyBox::LLGLSPipelineDepthTestSkyBox(bool depth_test, bool depth_write)
-: LLGLSPipelineSkyBox()
-, mDepth(depth_test ? GL_TRUE : GL_FALSE, depth_write ? GL_TRUE : GL_FALSE, GL_LEQUAL)
-{
-
-}
-
-LLGLSPipelineBlendSkyBox::LLGLSPipelineBlendSkyBox(bool depth_test, bool depth_write)
-: LLGLSPipelineDepthTestSkyBox(depth_test, depth_write)
-, mBlend(GL_BLEND)
-{
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
-}
-
-#if LL_WINDOWS
-// Expose desired use of high-performance graphics processor to Optimus driver and to AMD driver
-// https://docs.nvidia.com/gameworks/content/technologies/desktop/optimus.htm
-extern "C"
-{
- __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
- __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
-}
-#endif
-
-
+/**
+ * @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 "llglstates.h"
+#include "llrender.h"
+
+#include "llerror.h"
+#include "llerrorcontrol.h"
+#include "llquaternion.h"
+#include "llmath.h"
+#include "m4math.h"
+#include "llstring.h"
+#include "llstacktrace.h"
+
+#include "llglheaders.h"
+#include "llglslshader.h"
+
+#if LL_WINDOWS
+#include "lldxhardware.h"
+#endif
+
+#ifdef _DEBUG
+//#define GL_STATE_VERIFY
+#endif
+
+
+bool gDebugSession = false;
+bool gDebugGLSession = false;
+bool gClothRipple = false;
+bool gHeadlessClient = false;
+bool gNonInteractive = false;
+bool gGLActive = false;
+
+static const std::string HEADLESS_VENDOR_STRING("Linden Lab");
+static const std::string HEADLESS_RENDERER_STRING("Headless");
+static const std::string HEADLESS_VERSION_STRING("1.0");
+
+llofstream gFailLog;
+
+#if GL_ARB_debug_output
+
+#ifndef APIENTRY
+#define APIENTRY
+#endif
+
+void APIENTRY gl_debug_callback(GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar* message,
+ GLvoid* userParam)
+{
+ /*if (severity != GL_DEBUG_SEVERITY_HIGH &&
+ severity != GL_DEBUG_SEVERITY_MEDIUM &&
+ severity != GL_DEBUG_SEVERITY_LOW
+ )
+ { //suppress out-of-spec messages sent by nvidia driver (mostly vertexbuffer hints)
+ return;
+ }*/
+
+ if (gGLManager.mIsDisabled &&
+ severity == GL_DEBUG_SEVERITY_HIGH_ARB &&
+ source == GL_DEBUG_SOURCE_API_ARB &&
+ type == GL_DEBUG_TYPE_ERROR_ARB &&
+ id == GL_INVALID_VALUE)
+ {
+ // Suppress messages about deleting already deleted objects called from LLViewerWindow::stopGL()
+ // "GL_INVALID_VALUE error generated. Handle does not refer to an object generated by OpenGL."
+ return;
+ }
+
+ // list of messages to suppress
+ const char* suppress[] =
+ {
+ "Buffer detailed info:",
+ "Program undefined behavior warning: The current GL state uses a sampler (0) that has depth comparisons enabled"
+ };
+
+ for (const char* msg : suppress)
+ {
+ if (strncmp(msg, message, strlen(msg)) == 0)
+ {
+ return;
+ }
+ }
+
+ if (severity == GL_DEBUG_SEVERITY_HIGH)
+ {
+ LL_WARNS() << "----- GL ERROR --------" << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS() << "----- GL WARNING -------" << LL_ENDL;
+ }
+ LL_WARNS() << "Type: " << std::hex << type << LL_ENDL;
+ LL_WARNS() << "ID: " << std::hex << id << LL_ENDL;
+ LL_WARNS() << "Severity: " << std::hex << severity << LL_ENDL;
+ LL_WARNS() << "Message: " << message << LL_ENDL;
+ LL_WARNS() << "-----------------------" << LL_ENDL;
+
+ GLint vao = 0;
+ glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vao);
+ GLint vbo = 0;
+ glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo);
+ GLint vbo_size = 0;
+ if (vbo != 0)
+ {
+ glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &vbo_size);
+ }
+ GLint ibo = 0;
+ glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &ibo);
+ GLint ibo_size = 0;
+ if (ibo != 0)
+ {
+ glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &ibo_size);
+ }
+ GLint ubo = 0;
+ glGetIntegerv(GL_UNIFORM_BUFFER_BINDING, &ubo);
+ GLint ubo_size = 0;
+ GLint ubo_immutable = 0;
+ if (ubo != 0)
+ {
+ glGetBufferParameteriv(GL_UNIFORM_BUFFER, GL_BUFFER_SIZE, &ubo_size);
+ glGetBufferParameteriv(GL_UNIFORM_BUFFER, GL_BUFFER_IMMUTABLE_STORAGE, &ubo_immutable);
+ }
+
+ // No needs to halt when is called from LLViewerWindow::stopGL()
+ if (severity == GL_DEBUG_SEVERITY_HIGH && !gGLManager.mIsDisabled)
+ {
+ LL_ERRS() << "Halting on GL Error" << LL_ENDL;
+ }
+}
+#endif
+
+void parse_glsl_version(S32& major, S32& minor);
+
+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
+
+std::list<LLGLUpdate*> LLGLUpdate::sGLQ;
+
+#if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS
+
+#if LL_WINDOWS
+// WGL_ARB_create_context
+PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr;
+
+// WGL_AMD_gpu_association
+PFNWGLGETGPUIDSAMDPROC wglGetGPUIDsAMD = nullptr;
+PFNWGLGETGPUINFOAMDPROC wglGetGPUInfoAMD = nullptr;
+PFNWGLGETCONTEXTGPUIDAMDPROC wglGetContextGPUIDAMD = nullptr;
+PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC wglCreateAssociatedContextAMD = nullptr;
+PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC wglCreateAssociatedContextAttribsAMD = nullptr;
+PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC wglDeleteAssociatedContextAMD = nullptr;
+PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC wglMakeAssociatedContextCurrentAMD = nullptr;
+PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC wglGetCurrentAssociatedContextAMD = nullptr;
+PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC wglBlitContextFramebufferAMD = nullptr;
+
+// WGL_EXT_swap_control
+PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr;
+PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = nullptr;
+
+#endif
+
+// GL_VERSION_1_2
+//PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements = nullptr;
+//PFNGLTEXIMAGE3DPROC glTexImage3D = nullptr;
+//PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D = nullptr;
+//PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D = nullptr;
+
+// GL_VERSION_1_3
+PFNGLACTIVETEXTUREPROC glActiveTexture = nullptr;
+PFNGLSAMPLECOVERAGEPROC glSampleCoverage = nullptr;
+PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D = nullptr;
+PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D = nullptr;
+PFNGLCOMPRESSEDTEXIMAGE1DPROC glCompressedTexImage1D = nullptr;
+PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glCompressedTexSubImage3D = nullptr;
+PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2D = nullptr;
+PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glCompressedTexSubImage1D = nullptr;
+PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage = nullptr;
+PFNGLCLIENTACTIVETEXTUREPROC glClientActiveTexture = nullptr;
+PFNGLMULTITEXCOORD1DPROC glMultiTexCoord1d = nullptr;
+PFNGLMULTITEXCOORD1DVPROC glMultiTexCoord1dv = nullptr;
+PFNGLMULTITEXCOORD1FPROC glMultiTexCoord1f = nullptr;
+PFNGLMULTITEXCOORD1FVPROC glMultiTexCoord1fv = nullptr;
+PFNGLMULTITEXCOORD1IPROC glMultiTexCoord1i = nullptr;
+PFNGLMULTITEXCOORD1IVPROC glMultiTexCoord1iv = nullptr;
+PFNGLMULTITEXCOORD1SPROC glMultiTexCoord1s = nullptr;
+PFNGLMULTITEXCOORD1SVPROC glMultiTexCoord1sv = nullptr;
+PFNGLMULTITEXCOORD2DPROC glMultiTexCoord2d = nullptr;
+PFNGLMULTITEXCOORD2DVPROC glMultiTexCoord2dv = nullptr;
+PFNGLMULTITEXCOORD2FPROC glMultiTexCoord2f = nullptr;
+PFNGLMULTITEXCOORD2FVPROC glMultiTexCoord2fv = nullptr;
+PFNGLMULTITEXCOORD2IPROC glMultiTexCoord2i = nullptr;
+PFNGLMULTITEXCOORD2IVPROC glMultiTexCoord2iv = nullptr;
+PFNGLMULTITEXCOORD2SPROC glMultiTexCoord2s = nullptr;
+PFNGLMULTITEXCOORD2SVPROC glMultiTexCoord2sv = nullptr;
+PFNGLMULTITEXCOORD3DPROC glMultiTexCoord3d = nullptr;
+PFNGLMULTITEXCOORD3DVPROC glMultiTexCoord3dv = nullptr;
+PFNGLMULTITEXCOORD3FPROC glMultiTexCoord3f = nullptr;
+PFNGLMULTITEXCOORD3FVPROC glMultiTexCoord3fv = nullptr;
+PFNGLMULTITEXCOORD3IPROC glMultiTexCoord3i = nullptr;
+PFNGLMULTITEXCOORD3IVPROC glMultiTexCoord3iv = nullptr;
+PFNGLMULTITEXCOORD3SPROC glMultiTexCoord3s = nullptr;
+PFNGLMULTITEXCOORD3SVPROC glMultiTexCoord3sv = nullptr;
+PFNGLMULTITEXCOORD4DPROC glMultiTexCoord4d = nullptr;
+PFNGLMULTITEXCOORD4DVPROC glMultiTexCoord4dv = nullptr;
+PFNGLMULTITEXCOORD4FPROC glMultiTexCoord4f = nullptr;
+PFNGLMULTITEXCOORD4FVPROC glMultiTexCoord4fv = nullptr;
+PFNGLMULTITEXCOORD4IPROC glMultiTexCoord4i = nullptr;
+PFNGLMULTITEXCOORD4IVPROC glMultiTexCoord4iv = nullptr;
+PFNGLMULTITEXCOORD4SPROC glMultiTexCoord4s = nullptr;
+PFNGLMULTITEXCOORD4SVPROC glMultiTexCoord4sv = nullptr;
+PFNGLLOADTRANSPOSEMATRIXFPROC glLoadTransposeMatrixf = nullptr;
+PFNGLLOADTRANSPOSEMATRIXDPROC glLoadTransposeMatrixd = nullptr;
+PFNGLMULTTRANSPOSEMATRIXFPROC glMultTransposeMatrixf = nullptr;
+PFNGLMULTTRANSPOSEMATRIXDPROC glMultTransposeMatrixd = nullptr;
+
+// GL_VERSION_1_4
+PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate = nullptr;
+PFNGLMULTIDRAWARRAYSPROC glMultiDrawArrays = nullptr;
+PFNGLMULTIDRAWELEMENTSPROC glMultiDrawElements = nullptr;
+PFNGLPOINTPARAMETERFPROC glPointParameterf = nullptr;
+PFNGLPOINTPARAMETERFVPROC glPointParameterfv = nullptr;
+PFNGLPOINTPARAMETERIPROC glPointParameteri = nullptr;
+PFNGLPOINTPARAMETERIVPROC glPointParameteriv = nullptr;
+PFNGLFOGCOORDFPROC glFogCoordf = nullptr;
+PFNGLFOGCOORDFVPROC glFogCoordfv = nullptr;
+PFNGLFOGCOORDDPROC glFogCoordd = nullptr;
+PFNGLFOGCOORDDVPROC glFogCoorddv = nullptr;
+PFNGLFOGCOORDPOINTERPROC glFogCoordPointer = nullptr;
+PFNGLSECONDARYCOLOR3BPROC glSecondaryColor3b = nullptr;
+PFNGLSECONDARYCOLOR3BVPROC glSecondaryColor3bv = nullptr;
+PFNGLSECONDARYCOLOR3DPROC glSecondaryColor3d = nullptr;
+PFNGLSECONDARYCOLOR3DVPROC glSecondaryColor3dv = nullptr;
+PFNGLSECONDARYCOLOR3FPROC glSecondaryColor3f = nullptr;
+PFNGLSECONDARYCOLOR3FVPROC glSecondaryColor3fv = nullptr;
+PFNGLSECONDARYCOLOR3IPROC glSecondaryColor3i = nullptr;
+PFNGLSECONDARYCOLOR3IVPROC glSecondaryColor3iv = nullptr;
+PFNGLSECONDARYCOLOR3SPROC glSecondaryColor3s = nullptr;
+PFNGLSECONDARYCOLOR3SVPROC glSecondaryColor3sv = nullptr;
+PFNGLSECONDARYCOLOR3UBPROC glSecondaryColor3ub = nullptr;
+PFNGLSECONDARYCOLOR3UBVPROC glSecondaryColor3ubv = nullptr;
+PFNGLSECONDARYCOLOR3UIPROC glSecondaryColor3ui = nullptr;
+PFNGLSECONDARYCOLOR3UIVPROC glSecondaryColor3uiv = nullptr;
+PFNGLSECONDARYCOLOR3USPROC glSecondaryColor3us = nullptr;
+PFNGLSECONDARYCOLOR3USVPROC glSecondaryColor3usv = nullptr;
+PFNGLSECONDARYCOLORPOINTERPROC glSecondaryColorPointer = nullptr;
+PFNGLWINDOWPOS2DPROC glWindowPos2d = nullptr;
+PFNGLWINDOWPOS2DVPROC glWindowPos2dv = nullptr;
+PFNGLWINDOWPOS2FPROC glWindowPos2f = nullptr;
+PFNGLWINDOWPOS2FVPROC glWindowPos2fv = nullptr;
+PFNGLWINDOWPOS2IPROC glWindowPos2i = nullptr;
+PFNGLWINDOWPOS2IVPROC glWindowPos2iv = nullptr;
+PFNGLWINDOWPOS2SPROC glWindowPos2s = nullptr;
+PFNGLWINDOWPOS2SVPROC glWindowPos2sv = nullptr;
+PFNGLWINDOWPOS3DPROC glWindowPos3d = nullptr;
+PFNGLWINDOWPOS3DVPROC glWindowPos3dv = nullptr;
+PFNGLWINDOWPOS3FPROC glWindowPos3f = nullptr;
+PFNGLWINDOWPOS3FVPROC glWindowPos3fv = nullptr;
+PFNGLWINDOWPOS3IPROC glWindowPos3i = nullptr;
+PFNGLWINDOWPOS3IVPROC glWindowPos3iv = nullptr;
+PFNGLWINDOWPOS3SPROC glWindowPos3s = nullptr;
+PFNGLWINDOWPOS3SVPROC glWindowPos3sv = nullptr;
+
+// GL_VERSION_1_5
+PFNGLGENQUERIESPROC glGenQueries = nullptr;
+PFNGLDELETEQUERIESPROC glDeleteQueries = nullptr;
+PFNGLISQUERYPROC glIsQuery = nullptr;
+PFNGLBEGINQUERYPROC glBeginQuery = nullptr;
+PFNGLENDQUERYPROC glEndQuery = nullptr;
+PFNGLGETQUERYIVPROC glGetQueryiv = nullptr;
+PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv = nullptr;
+PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv = nullptr;
+PFNGLBINDBUFFERPROC glBindBuffer = nullptr;
+PFNGLDELETEBUFFERSPROC glDeleteBuffers = nullptr;
+PFNGLGENBUFFERSPROC glGenBuffers = nullptr;
+PFNGLISBUFFERPROC glIsBuffer = nullptr;
+PFNGLBUFFERDATAPROC glBufferData = nullptr;
+PFNGLBUFFERSUBDATAPROC glBufferSubData = nullptr;
+PFNGLGETBUFFERSUBDATAPROC glGetBufferSubData = nullptr;
+PFNGLMAPBUFFERPROC glMapBuffer = nullptr;
+PFNGLUNMAPBUFFERPROC glUnmapBuffer = nullptr;
+PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv = nullptr;
+PFNGLGETBUFFERPOINTERVPROC glGetBufferPointerv = nullptr;
+
+// GL_VERSION_2_0
+PFNGLBLENDEQUATIONSEPARATEPROC glBlendEquationSeparate = nullptr;
+PFNGLDRAWBUFFERSPROC glDrawBuffers = nullptr;
+PFNGLSTENCILOPSEPARATEPROC glStencilOpSeparate = nullptr;
+PFNGLSTENCILFUNCSEPARATEPROC glStencilFuncSeparate = nullptr;
+PFNGLSTENCILMASKSEPARATEPROC glStencilMaskSeparate = nullptr;
+PFNGLATTACHSHADERPROC glAttachShader = nullptr;
+PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation = nullptr;
+PFNGLCOMPILESHADERPROC glCompileShader = nullptr;
+PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr;
+PFNGLCREATESHADERPROC glCreateShader = nullptr;
+PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr;
+PFNGLDELETESHADERPROC glDeleteShader = nullptr;
+PFNGLDETACHSHADERPROC glDetachShader = nullptr;
+PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray = nullptr;
+PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr;
+PFNGLGETACTIVEATTRIBPROC glGetActiveAttrib = nullptr;
+PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform = nullptr;
+PFNGLGETATTACHEDSHADERSPROC glGetAttachedShaders = nullptr;
+PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation = nullptr;
+PFNGLGETPROGRAMIVPROC glGetProgramiv = nullptr;
+PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = nullptr;
+PFNGLGETSHADERIVPROC glGetShaderiv = nullptr;
+PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = nullptr;
+PFNGLGETSHADERSOURCEPROC glGetShaderSource = nullptr;
+PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr;
+PFNGLGETUNIFORMFVPROC glGetUniformfv = nullptr;
+PFNGLGETUNIFORMIVPROC glGetUniformiv = nullptr;
+PFNGLGETVERTEXATTRIBDVPROC glGetVertexAttribdv = nullptr;
+PFNGLGETVERTEXATTRIBFVPROC glGetVertexAttribfv = nullptr;
+PFNGLGETVERTEXATTRIBIVPROC glGetVertexAttribiv = nullptr;
+PFNGLGETVERTEXATTRIBPOINTERVPROC glGetVertexAttribPointerv = nullptr;
+PFNGLISPROGRAMPROC glIsProgram = nullptr;
+PFNGLISSHADERPROC glIsShader = nullptr;
+PFNGLLINKPROGRAMPROC glLinkProgram = nullptr;
+PFNGLSHADERSOURCEPROC glShaderSource = nullptr;
+PFNGLUSEPROGRAMPROC glUseProgram = nullptr;
+PFNGLUNIFORM1FPROC glUniform1f = nullptr;
+PFNGLUNIFORM2FPROC glUniform2f = nullptr;
+PFNGLUNIFORM3FPROC glUniform3f = nullptr;
+PFNGLUNIFORM4FPROC glUniform4f = nullptr;
+PFNGLUNIFORM1IPROC glUniform1i = nullptr;
+PFNGLUNIFORM2IPROC glUniform2i = nullptr;
+PFNGLUNIFORM3IPROC glUniform3i = nullptr;
+PFNGLUNIFORM4IPROC glUniform4i = nullptr;
+PFNGLUNIFORM1FVPROC glUniform1fv = nullptr;
+PFNGLUNIFORM2FVPROC glUniform2fv = nullptr;
+PFNGLUNIFORM3FVPROC glUniform3fv = nullptr;
+PFNGLUNIFORM4FVPROC glUniform4fv = nullptr;
+PFNGLUNIFORM1IVPROC glUniform1iv = nullptr;
+PFNGLUNIFORM2IVPROC glUniform2iv = nullptr;
+PFNGLUNIFORM3IVPROC glUniform3iv = nullptr;
+PFNGLUNIFORM4IVPROC glUniform4iv = nullptr;
+PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv = nullptr;
+PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv = nullptr;
+PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = nullptr;
+PFNGLVALIDATEPROGRAMPROC glValidateProgram = nullptr;
+PFNGLVERTEXATTRIB1DPROC glVertexAttrib1d = nullptr;
+PFNGLVERTEXATTRIB1DVPROC glVertexAttrib1dv = nullptr;
+PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f = nullptr;
+PFNGLVERTEXATTRIB1FVPROC glVertexAttrib1fv = nullptr;
+PFNGLVERTEXATTRIB1SPROC glVertexAttrib1s = nullptr;
+PFNGLVERTEXATTRIB1SVPROC glVertexAttrib1sv = nullptr;
+PFNGLVERTEXATTRIB2DPROC glVertexAttrib2d = nullptr;
+PFNGLVERTEXATTRIB2DVPROC glVertexAttrib2dv = nullptr;
+PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f = nullptr;
+PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv = nullptr;
+PFNGLVERTEXATTRIB2SPROC glVertexAttrib2s = nullptr;
+PFNGLVERTEXATTRIB2SVPROC glVertexAttrib2sv = nullptr;
+PFNGLVERTEXATTRIB3DPROC glVertexAttrib3d = nullptr;
+PFNGLVERTEXATTRIB3DVPROC glVertexAttrib3dv = nullptr;
+PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f = nullptr;
+PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv = nullptr;
+PFNGLVERTEXATTRIB3SPROC glVertexAttrib3s = nullptr;
+PFNGLVERTEXATTRIB3SVPROC glVertexAttrib3sv = nullptr;
+PFNGLVERTEXATTRIB4NBVPROC glVertexAttrib4Nbv = nullptr;
+PFNGLVERTEXATTRIB4NIVPROC glVertexAttrib4Niv = nullptr;
+PFNGLVERTEXATTRIB4NSVPROC glVertexAttrib4Nsv = nullptr;
+PFNGLVERTEXATTRIB4NUBPROC glVertexAttrib4Nub = nullptr;
+PFNGLVERTEXATTRIB4NUBVPROC glVertexAttrib4Nubv = nullptr;
+PFNGLVERTEXATTRIB4NUIVPROC glVertexAttrib4Nuiv = nullptr;
+PFNGLVERTEXATTRIB4NUSVPROC glVertexAttrib4Nusv = nullptr;
+PFNGLVERTEXATTRIB4BVPROC glVertexAttrib4bv = nullptr;
+PFNGLVERTEXATTRIB4DPROC glVertexAttrib4d = nullptr;
+PFNGLVERTEXATTRIB4DVPROC glVertexAttrib4dv = nullptr;
+PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f = nullptr;
+PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv = nullptr;
+PFNGLVERTEXATTRIB4IVPROC glVertexAttrib4iv = nullptr;
+PFNGLVERTEXATTRIB4SPROC glVertexAttrib4s = nullptr;
+PFNGLVERTEXATTRIB4SVPROC glVertexAttrib4sv = nullptr;
+PFNGLVERTEXATTRIB4UBVPROC glVertexAttrib4ubv = nullptr;
+PFNGLVERTEXATTRIB4UIVPROC glVertexAttrib4uiv = nullptr;
+PFNGLVERTEXATTRIB4USVPROC glVertexAttrib4usv = nullptr;
+PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr;
+
+// GL_VERSION_2_1
+PFNGLUNIFORMMATRIX2X3FVPROC glUniformMatrix2x3fv = nullptr;
+PFNGLUNIFORMMATRIX3X2FVPROC glUniformMatrix3x2fv = nullptr;
+PFNGLUNIFORMMATRIX2X4FVPROC glUniformMatrix2x4fv = nullptr;
+PFNGLUNIFORMMATRIX4X2FVPROC glUniformMatrix4x2fv = nullptr;
+PFNGLUNIFORMMATRIX3X4FVPROC glUniformMatrix3x4fv = nullptr;
+PFNGLUNIFORMMATRIX4X3FVPROC glUniformMatrix4x3fv = nullptr;
+
+// GL_VERSION_3_0
+PFNGLCOLORMASKIPROC glColorMaski = nullptr;
+PFNGLGETBOOLEANI_VPROC glGetBooleani_v = nullptr;
+PFNGLGETINTEGERI_VPROC glGetIntegeri_v = nullptr;
+PFNGLENABLEIPROC glEnablei = nullptr;
+PFNGLDISABLEIPROC glDisablei = nullptr;
+PFNGLISENABLEDIPROC glIsEnabledi = nullptr;
+PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback = nullptr;
+PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback = nullptr;
+PFNGLBINDBUFFERRANGEPROC glBindBufferRange = nullptr;
+PFNGLBINDBUFFERBASEPROC glBindBufferBase = nullptr;
+PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings = nullptr;
+PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glGetTransformFeedbackVarying = nullptr;
+PFNGLCLAMPCOLORPROC glClampColor = nullptr;
+PFNGLBEGINCONDITIONALRENDERPROC glBeginConditionalRender = nullptr;
+PFNGLENDCONDITIONALRENDERPROC glEndConditionalRender = nullptr;
+PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer = nullptr;
+PFNGLGETVERTEXATTRIBIIVPROC glGetVertexAttribIiv = nullptr;
+PFNGLGETVERTEXATTRIBIUIVPROC glGetVertexAttribIuiv = nullptr;
+PFNGLVERTEXATTRIBI1IPROC glVertexAttribI1i = nullptr;
+PFNGLVERTEXATTRIBI2IPROC glVertexAttribI2i = nullptr;
+PFNGLVERTEXATTRIBI3IPROC glVertexAttribI3i = nullptr;
+PFNGLVERTEXATTRIBI4IPROC glVertexAttribI4i = nullptr;
+PFNGLVERTEXATTRIBI1UIPROC glVertexAttribI1ui = nullptr;
+PFNGLVERTEXATTRIBI2UIPROC glVertexAttribI2ui = nullptr;
+PFNGLVERTEXATTRIBI3UIPROC glVertexAttribI3ui = nullptr;
+PFNGLVERTEXATTRIBI4UIPROC glVertexAttribI4ui = nullptr;
+PFNGLVERTEXATTRIBI1IVPROC glVertexAttribI1iv = nullptr;
+PFNGLVERTEXATTRIBI2IVPROC glVertexAttribI2iv = nullptr;
+PFNGLVERTEXATTRIBI3IVPROC glVertexAttribI3iv = nullptr;
+PFNGLVERTEXATTRIBI4IVPROC glVertexAttribI4iv = nullptr;
+PFNGLVERTEXATTRIBI1UIVPROC glVertexAttribI1uiv = nullptr;
+PFNGLVERTEXATTRIBI2UIVPROC glVertexAttribI2uiv = nullptr;
+PFNGLVERTEXATTRIBI3UIVPROC glVertexAttribI3uiv = nullptr;
+PFNGLVERTEXATTRIBI4UIVPROC glVertexAttribI4uiv = nullptr;
+PFNGLVERTEXATTRIBI4BVPROC glVertexAttribI4bv = nullptr;
+PFNGLVERTEXATTRIBI4SVPROC glVertexAttribI4sv = nullptr;
+PFNGLVERTEXATTRIBI4UBVPROC glVertexAttribI4ubv = nullptr;
+PFNGLVERTEXATTRIBI4USVPROC glVertexAttribI4usv = nullptr;
+PFNGLGETUNIFORMUIVPROC glGetUniformuiv = nullptr;
+PFNGLBINDFRAGDATALOCATIONPROC glBindFragDataLocation = nullptr;
+PFNGLGETFRAGDATALOCATIONPROC glGetFragDataLocation = nullptr;
+PFNGLUNIFORM1UIPROC glUniform1ui = nullptr;
+PFNGLUNIFORM2UIPROC glUniform2ui = nullptr;
+PFNGLUNIFORM3UIPROC glUniform3ui = nullptr;
+PFNGLUNIFORM4UIPROC glUniform4ui = nullptr;
+PFNGLUNIFORM1UIVPROC glUniform1uiv = nullptr;
+PFNGLUNIFORM2UIVPROC glUniform2uiv = nullptr;
+PFNGLUNIFORM3UIVPROC glUniform3uiv = nullptr;
+PFNGLUNIFORM4UIVPROC glUniform4uiv = nullptr;
+PFNGLTEXPARAMETERIIVPROC glTexParameterIiv = nullptr;
+PFNGLTEXPARAMETERIUIVPROC glTexParameterIuiv = nullptr;
+PFNGLGETTEXPARAMETERIIVPROC glGetTexParameterIiv = nullptr;
+PFNGLGETTEXPARAMETERIUIVPROC glGetTexParameterIuiv = nullptr;
+PFNGLCLEARBUFFERIVPROC glClearBufferiv = nullptr;
+PFNGLCLEARBUFFERUIVPROC glClearBufferuiv = nullptr;
+PFNGLCLEARBUFFERFVPROC glClearBufferfv = nullptr;
+PFNGLCLEARBUFFERFIPROC glClearBufferfi = nullptr;
+PFNGLGETSTRINGIPROC glGetStringi = nullptr;
+PFNGLISRENDERBUFFERPROC glIsRenderbuffer = nullptr;
+PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer = nullptr;
+PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers = nullptr;
+PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers = nullptr;
+PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = nullptr;
+PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv = nullptr;
+PFNGLISFRAMEBUFFERPROC glIsFramebuffer = nullptr;
+PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = nullptr;
+PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = nullptr;
+PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = nullptr;
+PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus = nullptr;
+PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D = nullptr;
+PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = nullptr;
+PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D = nullptr;
+PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer = nullptr;
+PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv = nullptr;
+PFNGLGENERATEMIPMAPPROC glGenerateMipmap = nullptr;
+PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer = nullptr;
+PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample = nullptr;
+PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer = nullptr;
+PFNGLMAPBUFFERRANGEPROC glMapBufferRange = nullptr;
+PFNGLFLUSHMAPPEDBUFFERRANGEPROC glFlushMappedBufferRange = nullptr;
+PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr;
+PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = nullptr;
+PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr;
+PFNGLISVERTEXARRAYPROC glIsVertexArray = nullptr;
+
+// GL_VERSION_3_1
+PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced = nullptr;
+PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced = nullptr;
+PFNGLTEXBUFFERPROC glTexBuffer = nullptr;
+PFNGLPRIMITIVERESTARTINDEXPROC glPrimitiveRestartIndex = nullptr;
+PFNGLCOPYBUFFERSUBDATAPROC glCopyBufferSubData = nullptr;
+PFNGLGETUNIFORMINDICESPROC glGetUniformIndices = nullptr;
+PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv = nullptr;
+PFNGLGETACTIVEUNIFORMNAMEPROC glGetActiveUniformName = nullptr;
+PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex = nullptr;
+PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv = nullptr;
+PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glGetActiveUniformBlockName = nullptr;
+PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding = nullptr;
+
+// GL_VERSION_3_2
+PFNGLDRAWELEMENTSBASEVERTEXPROC glDrawElementsBaseVertex = nullptr;
+PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glDrawRangeElementsBaseVertex = nullptr;
+PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glDrawElementsInstancedBaseVertex = nullptr;
+PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glMultiDrawElementsBaseVertex = nullptr;
+PFNGLPROVOKINGVERTEXPROC glProvokingVertex = nullptr;
+PFNGLFENCESYNCPROC glFenceSync = nullptr;
+PFNGLISSYNCPROC glIsSync = nullptr;
+PFNGLDELETESYNCPROC glDeleteSync = nullptr;
+PFNGLCLIENTWAITSYNCPROC glClientWaitSync = nullptr;
+PFNGLWAITSYNCPROC glWaitSync = nullptr;
+PFNGLGETINTEGER64VPROC glGetInteger64v = nullptr;
+PFNGLGETSYNCIVPROC glGetSynciv = nullptr;
+PFNGLGETINTEGER64I_VPROC glGetInteger64i_v = nullptr;
+PFNGLGETBUFFERPARAMETERI64VPROC glGetBufferParameteri64v = nullptr;
+PFNGLFRAMEBUFFERTEXTUREPROC glFramebufferTexture = nullptr;
+PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample = nullptr;
+PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample = nullptr;
+PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv = nullptr;
+PFNGLSAMPLEMASKIPROC glSampleMaski = nullptr;
+
+// GL_VERSION_3_3
+PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glBindFragDataLocationIndexed = nullptr;
+PFNGLGETFRAGDATAINDEXPROC glGetFragDataIndex = nullptr;
+PFNGLGENSAMPLERSPROC glGenSamplers = nullptr;
+PFNGLDELETESAMPLERSPROC glDeleteSamplers = nullptr;
+PFNGLISSAMPLERPROC glIsSampler = nullptr;
+PFNGLBINDSAMPLERPROC glBindSampler = nullptr;
+PFNGLSAMPLERPARAMETERIPROC glSamplerParameteri = nullptr;
+PFNGLSAMPLERPARAMETERIVPROC glSamplerParameteriv = nullptr;
+PFNGLSAMPLERPARAMETERFPROC glSamplerParameterf = nullptr;
+PFNGLSAMPLERPARAMETERFVPROC glSamplerParameterfv = nullptr;
+PFNGLSAMPLERPARAMETERIIVPROC glSamplerParameterIiv = nullptr;
+PFNGLSAMPLERPARAMETERIUIVPROC glSamplerParameterIuiv = nullptr;
+PFNGLGETSAMPLERPARAMETERIVPROC glGetSamplerParameteriv = nullptr;
+PFNGLGETSAMPLERPARAMETERIIVPROC glGetSamplerParameterIiv = nullptr;
+PFNGLGETSAMPLERPARAMETERFVPROC glGetSamplerParameterfv = nullptr;
+PFNGLGETSAMPLERPARAMETERIUIVPROC glGetSamplerParameterIuiv = nullptr;
+PFNGLQUERYCOUNTERPROC glQueryCounter = nullptr;
+PFNGLGETQUERYOBJECTI64VPROC glGetQueryObjecti64v = nullptr;
+PFNGLGETQUERYOBJECTUI64VPROC glGetQueryObjectui64v = nullptr;
+PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor = nullptr;
+PFNGLVERTEXATTRIBP1UIPROC glVertexAttribP1ui = nullptr;
+PFNGLVERTEXATTRIBP1UIVPROC glVertexAttribP1uiv = nullptr;
+PFNGLVERTEXATTRIBP2UIPROC glVertexAttribP2ui = nullptr;
+PFNGLVERTEXATTRIBP2UIVPROC glVertexAttribP2uiv = nullptr;
+PFNGLVERTEXATTRIBP3UIPROC glVertexAttribP3ui = nullptr;
+PFNGLVERTEXATTRIBP3UIVPROC glVertexAttribP3uiv = nullptr;
+PFNGLVERTEXATTRIBP4UIPROC glVertexAttribP4ui = nullptr;
+PFNGLVERTEXATTRIBP4UIVPROC glVertexAttribP4uiv = nullptr;
+PFNGLVERTEXP2UIPROC glVertexP2ui = nullptr;
+PFNGLVERTEXP2UIVPROC glVertexP2uiv = nullptr;
+PFNGLVERTEXP3UIPROC glVertexP3ui = nullptr;
+PFNGLVERTEXP3UIVPROC glVertexP3uiv = nullptr;
+PFNGLVERTEXP4UIPROC glVertexP4ui = nullptr;
+PFNGLVERTEXP4UIVPROC glVertexP4uiv = nullptr;
+PFNGLTEXCOORDP1UIPROC glTexCoordP1ui = nullptr;
+PFNGLTEXCOORDP1UIVPROC glTexCoordP1uiv = nullptr;
+PFNGLTEXCOORDP2UIPROC glTexCoordP2ui = nullptr;
+PFNGLTEXCOORDP2UIVPROC glTexCoordP2uiv = nullptr;
+PFNGLTEXCOORDP3UIPROC glTexCoordP3ui = nullptr;
+PFNGLTEXCOORDP3UIVPROC glTexCoordP3uiv = nullptr;
+PFNGLTEXCOORDP4UIPROC glTexCoordP4ui = nullptr;
+PFNGLTEXCOORDP4UIVPROC glTexCoordP4uiv = nullptr;
+PFNGLMULTITEXCOORDP1UIPROC glMultiTexCoordP1ui = nullptr;
+PFNGLMULTITEXCOORDP1UIVPROC glMultiTexCoordP1uiv = nullptr;
+PFNGLMULTITEXCOORDP2UIPROC glMultiTexCoordP2ui = nullptr;
+PFNGLMULTITEXCOORDP2UIVPROC glMultiTexCoordP2uiv = nullptr;
+PFNGLMULTITEXCOORDP3UIPROC glMultiTexCoordP3ui = nullptr;
+PFNGLMULTITEXCOORDP3UIVPROC glMultiTexCoordP3uiv = nullptr;
+PFNGLMULTITEXCOORDP4UIPROC glMultiTexCoordP4ui = nullptr;
+PFNGLMULTITEXCOORDP4UIVPROC glMultiTexCoordP4uiv = nullptr;
+PFNGLNORMALP3UIPROC glNormalP3ui = nullptr;
+PFNGLNORMALP3UIVPROC glNormalP3uiv = nullptr;
+PFNGLCOLORP3UIPROC glColorP3ui = nullptr;
+PFNGLCOLORP3UIVPROC glColorP3uiv = nullptr;
+PFNGLCOLORP4UIPROC glColorP4ui = nullptr;
+PFNGLCOLORP4UIVPROC glColorP4uiv = nullptr;
+PFNGLSECONDARYCOLORP3UIPROC glSecondaryColorP3ui = nullptr;
+PFNGLSECONDARYCOLORP3UIVPROC glSecondaryColorP3uiv = nullptr;
+
+// GL_VERSION_4_0
+PFNGLMINSAMPLESHADINGPROC glMinSampleShading = nullptr;
+PFNGLBLENDEQUATIONIPROC glBlendEquationi = nullptr;
+PFNGLBLENDEQUATIONSEPARATEIPROC glBlendEquationSeparatei = nullptr;
+PFNGLBLENDFUNCIPROC glBlendFunci = nullptr;
+PFNGLBLENDFUNCSEPARATEIPROC glBlendFuncSeparatei = nullptr;
+PFNGLDRAWARRAYSINDIRECTPROC glDrawArraysIndirect = nullptr;
+PFNGLDRAWELEMENTSINDIRECTPROC glDrawElementsIndirect = nullptr;
+PFNGLUNIFORM1DPROC glUniform1d = nullptr;
+PFNGLUNIFORM2DPROC glUniform2d = nullptr;
+PFNGLUNIFORM3DPROC glUniform3d = nullptr;
+PFNGLUNIFORM4DPROC glUniform4d = nullptr;
+PFNGLUNIFORM1DVPROC glUniform1dv = nullptr;
+PFNGLUNIFORM2DVPROC glUniform2dv = nullptr;
+PFNGLUNIFORM3DVPROC glUniform3dv = nullptr;
+PFNGLUNIFORM4DVPROC glUniform4dv = nullptr;
+PFNGLUNIFORMMATRIX2DVPROC glUniformMatrix2dv = nullptr;
+PFNGLUNIFORMMATRIX3DVPROC glUniformMatrix3dv = nullptr;
+PFNGLUNIFORMMATRIX4DVPROC glUniformMatrix4dv = nullptr;
+PFNGLUNIFORMMATRIX2X3DVPROC glUniformMatrix2x3dv = nullptr;
+PFNGLUNIFORMMATRIX2X4DVPROC glUniformMatrix2x4dv = nullptr;
+PFNGLUNIFORMMATRIX3X2DVPROC glUniformMatrix3x2dv = nullptr;
+PFNGLUNIFORMMATRIX3X4DVPROC glUniformMatrix3x4dv = nullptr;
+PFNGLUNIFORMMATRIX4X2DVPROC glUniformMatrix4x2dv = nullptr;
+PFNGLUNIFORMMATRIX4X3DVPROC glUniformMatrix4x3dv = nullptr;
+PFNGLGETUNIFORMDVPROC glGetUniformdv = nullptr;
+PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC glGetSubroutineUniformLocation = nullptr;
+PFNGLGETSUBROUTINEINDEXPROC glGetSubroutineIndex = nullptr;
+PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC glGetActiveSubroutineUniformiv = nullptr;
+PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC glGetActiveSubroutineUniformName = nullptr;
+PFNGLGETACTIVESUBROUTINENAMEPROC glGetActiveSubroutineName = nullptr;
+PFNGLUNIFORMSUBROUTINESUIVPROC glUniformSubroutinesuiv = nullptr;
+PFNGLGETUNIFORMSUBROUTINEUIVPROC glGetUniformSubroutineuiv = nullptr;
+PFNGLGETPROGRAMSTAGEIVPROC glGetProgramStageiv = nullptr;
+PFNGLPATCHPARAMETERIPROC glPatchParameteri = nullptr;
+PFNGLPATCHPARAMETERFVPROC glPatchParameterfv = nullptr;
+PFNGLBINDTRANSFORMFEEDBACKPROC glBindTransformFeedback = nullptr;
+PFNGLDELETETRANSFORMFEEDBACKSPROC glDeleteTransformFeedbacks = nullptr;
+PFNGLGENTRANSFORMFEEDBACKSPROC glGenTransformFeedbacks = nullptr;
+PFNGLISTRANSFORMFEEDBACKPROC glIsTransformFeedback = nullptr;
+PFNGLPAUSETRANSFORMFEEDBACKPROC glPauseTransformFeedback = nullptr;
+PFNGLRESUMETRANSFORMFEEDBACKPROC glResumeTransformFeedback = nullptr;
+PFNGLDRAWTRANSFORMFEEDBACKPROC glDrawTransformFeedback = nullptr;
+PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glDrawTransformFeedbackStream = nullptr;
+PFNGLBEGINQUERYINDEXEDPROC glBeginQueryIndexed = nullptr;
+PFNGLENDQUERYINDEXEDPROC glEndQueryIndexed = nullptr;
+PFNGLGETQUERYINDEXEDIVPROC glGetQueryIndexediv = nullptr;
+
+// GL_VERSION_4_1
+PFNGLRELEASESHADERCOMPILERPROC glReleaseShaderCompiler = nullptr;
+PFNGLSHADERBINARYPROC glShaderBinary = nullptr;
+PFNGLGETSHADERPRECISIONFORMATPROC glGetShaderPrecisionFormat = nullptr;
+PFNGLDEPTHRANGEFPROC glDepthRangef = nullptr;
+PFNGLCLEARDEPTHFPROC glClearDepthf = nullptr;
+PFNGLGETPROGRAMBINARYPROC glGetProgramBinary = nullptr;
+PFNGLPROGRAMBINARYPROC glProgramBinary = nullptr;
+PFNGLPROGRAMPARAMETERIPROC glProgramParameteri = nullptr;
+PFNGLUSEPROGRAMSTAGESPROC glUseProgramStages = nullptr;
+PFNGLACTIVESHADERPROGRAMPROC glActiveShaderProgram = nullptr;
+PFNGLCREATESHADERPROGRAMVPROC glCreateShaderProgramv = nullptr;
+PFNGLBINDPROGRAMPIPELINEPROC glBindProgramPipeline = nullptr;
+PFNGLDELETEPROGRAMPIPELINESPROC glDeleteProgramPipelines = nullptr;
+PFNGLGENPROGRAMPIPELINESPROC glGenProgramPipelines = nullptr;
+PFNGLISPROGRAMPIPELINEPROC glIsProgramPipeline = nullptr;
+PFNGLGETPROGRAMPIPELINEIVPROC glGetProgramPipelineiv = nullptr;
+PFNGLPROGRAMUNIFORM1IPROC glProgramUniform1i = nullptr;
+PFNGLPROGRAMUNIFORM1IVPROC glProgramUniform1iv = nullptr;
+PFNGLPROGRAMUNIFORM1FPROC glProgramUniform1f = nullptr;
+PFNGLPROGRAMUNIFORM1FVPROC glProgramUniform1fv = nullptr;
+PFNGLPROGRAMUNIFORM1DPROC glProgramUniform1d = nullptr;
+PFNGLPROGRAMUNIFORM1DVPROC glProgramUniform1dv = nullptr;
+PFNGLPROGRAMUNIFORM1UIPROC glProgramUniform1ui = nullptr;
+PFNGLPROGRAMUNIFORM1UIVPROC glProgramUniform1uiv = nullptr;
+PFNGLPROGRAMUNIFORM2IPROC glProgramUniform2i = nullptr;
+PFNGLPROGRAMUNIFORM2IVPROC glProgramUniform2iv = nullptr;
+PFNGLPROGRAMUNIFORM2FPROC glProgramUniform2f = nullptr;
+PFNGLPROGRAMUNIFORM2FVPROC glProgramUniform2fv = nullptr;
+PFNGLPROGRAMUNIFORM2DPROC glProgramUniform2d = nullptr;
+PFNGLPROGRAMUNIFORM2DVPROC glProgramUniform2dv = nullptr;
+PFNGLPROGRAMUNIFORM2UIPROC glProgramUniform2ui = nullptr;
+PFNGLPROGRAMUNIFORM2UIVPROC glProgramUniform2uiv = nullptr;
+PFNGLPROGRAMUNIFORM3IPROC glProgramUniform3i = nullptr;
+PFNGLPROGRAMUNIFORM3IVPROC glProgramUniform3iv = nullptr;
+PFNGLPROGRAMUNIFORM3FPROC glProgramUniform3f = nullptr;
+PFNGLPROGRAMUNIFORM3FVPROC glProgramUniform3fv = nullptr;
+PFNGLPROGRAMUNIFORM3DPROC glProgramUniform3d = nullptr;
+PFNGLPROGRAMUNIFORM3DVPROC glProgramUniform3dv = nullptr;
+PFNGLPROGRAMUNIFORM3UIPROC glProgramUniform3ui = nullptr;
+PFNGLPROGRAMUNIFORM3UIVPROC glProgramUniform3uiv = nullptr;
+PFNGLPROGRAMUNIFORM4IPROC glProgramUniform4i = nullptr;
+PFNGLPROGRAMUNIFORM4IVPROC glProgramUniform4iv = nullptr;
+PFNGLPROGRAMUNIFORM4FPROC glProgramUniform4f = nullptr;
+PFNGLPROGRAMUNIFORM4FVPROC glProgramUniform4fv = nullptr;
+PFNGLPROGRAMUNIFORM4DPROC glProgramUniform4d = nullptr;
+PFNGLPROGRAMUNIFORM4DVPROC glProgramUniform4dv = nullptr;
+PFNGLPROGRAMUNIFORM4UIPROC glProgramUniform4ui = nullptr;
+PFNGLPROGRAMUNIFORM4UIVPROC glProgramUniform4uiv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX2FVPROC glProgramUniformMatrix2fv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX3FVPROC glProgramUniformMatrix3fv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX4FVPROC glProgramUniformMatrix4fv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX2DVPROC glProgramUniformMatrix2dv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX3DVPROC glProgramUniformMatrix3dv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX4DVPROC glProgramUniformMatrix4dv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glProgramUniformMatrix2x3fv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glProgramUniformMatrix3x2fv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glProgramUniformMatrix2x4fv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glProgramUniformMatrix4x2fv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glProgramUniformMatrix3x4fv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glProgramUniformMatrix4x3fv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC glProgramUniformMatrix2x3dv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC glProgramUniformMatrix3x2dv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC glProgramUniformMatrix2x4dv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC glProgramUniformMatrix4x2dv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC glProgramUniformMatrix3x4dv = nullptr;
+PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC glProgramUniformMatrix4x3dv = nullptr;
+PFNGLVALIDATEPROGRAMPIPELINEPROC glValidateProgramPipeline = nullptr;
+PFNGLGETPROGRAMPIPELINEINFOLOGPROC glGetProgramPipelineInfoLog = nullptr;
+PFNGLVERTEXATTRIBL1DPROC glVertexAttribL1d = nullptr;
+PFNGLVERTEXATTRIBL2DPROC glVertexAttribL2d = nullptr;
+PFNGLVERTEXATTRIBL3DPROC glVertexAttribL3d = nullptr;
+PFNGLVERTEXATTRIBL4DPROC glVertexAttribL4d = nullptr;
+PFNGLVERTEXATTRIBL1DVPROC glVertexAttribL1dv = nullptr;
+PFNGLVERTEXATTRIBL2DVPROC glVertexAttribL2dv = nullptr;
+PFNGLVERTEXATTRIBL3DVPROC glVertexAttribL3dv = nullptr;
+PFNGLVERTEXATTRIBL4DVPROC glVertexAttribL4dv = nullptr;
+PFNGLVERTEXATTRIBLPOINTERPROC glVertexAttribLPointer = nullptr;
+PFNGLGETVERTEXATTRIBLDVPROC glGetVertexAttribLdv = nullptr;
+PFNGLVIEWPORTARRAYVPROC glViewportArrayv = nullptr;
+PFNGLVIEWPORTINDEXEDFPROC glViewportIndexedf = nullptr;
+PFNGLVIEWPORTINDEXEDFVPROC glViewportIndexedfv = nullptr;
+PFNGLSCISSORARRAYVPROC glScissorArrayv = nullptr;
+PFNGLSCISSORINDEXEDPROC glScissorIndexed = nullptr;
+PFNGLSCISSORINDEXEDVPROC glScissorIndexedv = nullptr;
+PFNGLDEPTHRANGEARRAYVPROC glDepthRangeArrayv = nullptr;
+PFNGLDEPTHRANGEINDEXEDPROC glDepthRangeIndexed = nullptr;
+PFNGLGETFLOATI_VPROC glGetFloati_v = nullptr;
+PFNGLGETDOUBLEI_VPROC glGetDoublei_v = nullptr;
+
+// GL_VERSION_4_2
+PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glDrawArraysInstancedBaseInstance = nullptr;
+PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glDrawElementsInstancedBaseInstance = nullptr;
+PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glDrawElementsInstancedBaseVertexBaseInstance = nullptr;
+PFNGLGETINTERNALFORMATIVPROC glGetInternalformativ = nullptr;
+PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glGetActiveAtomicCounterBufferiv = nullptr;
+PFNGLBINDIMAGETEXTUREPROC glBindImageTexture = nullptr;
+PFNGLMEMORYBARRIERPROC glMemoryBarrier = nullptr;
+PFNGLTEXSTORAGE1DPROC glTexStorage1D = nullptr;
+PFNGLTEXSTORAGE2DPROC glTexStorage2D = nullptr;
+PFNGLTEXSTORAGE3DPROC glTexStorage3D = nullptr;
+PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glDrawTransformFeedbackInstanced = nullptr;
+PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glDrawTransformFeedbackStreamInstanced = nullptr;
+
+// GL_VERSION_4_3
+PFNGLCLEARBUFFERDATAPROC glClearBufferData = nullptr;
+PFNGLCLEARBUFFERSUBDATAPROC glClearBufferSubData = nullptr;
+PFNGLDISPATCHCOMPUTEPROC glDispatchCompute = nullptr;
+PFNGLDISPATCHCOMPUTEINDIRECTPROC glDispatchComputeIndirect = nullptr;
+PFNGLCOPYIMAGESUBDATAPROC glCopyImageSubData = nullptr;
+PFNGLFRAMEBUFFERPARAMETERIPROC glFramebufferParameteri = nullptr;
+PFNGLGETFRAMEBUFFERPARAMETERIVPROC glGetFramebufferParameteriv = nullptr;
+PFNGLGETINTERNALFORMATI64VPROC glGetInternalformati64v = nullptr;
+PFNGLINVALIDATETEXSUBIMAGEPROC glInvalidateTexSubImage = nullptr;
+PFNGLINVALIDATETEXIMAGEPROC glInvalidateTexImage = nullptr;
+PFNGLINVALIDATEBUFFERSUBDATAPROC glInvalidateBufferSubData = nullptr;
+PFNGLINVALIDATEBUFFERDATAPROC glInvalidateBufferData = nullptr;
+PFNGLINVALIDATEFRAMEBUFFERPROC glInvalidateFramebuffer = nullptr;
+PFNGLINVALIDATESUBFRAMEBUFFERPROC glInvalidateSubFramebuffer = nullptr;
+PFNGLMULTIDRAWARRAYSINDIRECTPROC glMultiDrawArraysIndirect = nullptr;
+PFNGLMULTIDRAWELEMENTSINDIRECTPROC glMultiDrawElementsIndirect = nullptr;
+PFNGLGETPROGRAMINTERFACEIVPROC glGetProgramInterfaceiv = nullptr;
+PFNGLGETPROGRAMRESOURCEINDEXPROC glGetProgramResourceIndex = nullptr;
+PFNGLGETPROGRAMRESOURCENAMEPROC glGetProgramResourceName = nullptr;
+PFNGLGETPROGRAMRESOURCEIVPROC glGetProgramResourceiv = nullptr;
+PFNGLGETPROGRAMRESOURCELOCATIONPROC glGetProgramResourceLocation = nullptr;
+PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC glGetProgramResourceLocationIndex = nullptr;
+PFNGLSHADERSTORAGEBLOCKBINDINGPROC glShaderStorageBlockBinding = nullptr;
+PFNGLTEXBUFFERRANGEPROC glTexBufferRange = nullptr;
+PFNGLTEXSTORAGE2DMULTISAMPLEPROC glTexStorage2DMultisample = nullptr;
+PFNGLTEXSTORAGE3DMULTISAMPLEPROC glTexStorage3DMultisample = nullptr;
+PFNGLTEXTUREVIEWPROC glTextureView = nullptr;
+PFNGLBINDVERTEXBUFFERPROC glBindVertexBuffer = nullptr;
+PFNGLVERTEXATTRIBFORMATPROC glVertexAttribFormat = nullptr;
+PFNGLVERTEXATTRIBIFORMATPROC glVertexAttribIFormat = nullptr;
+PFNGLVERTEXATTRIBLFORMATPROC glVertexAttribLFormat = nullptr;
+PFNGLVERTEXATTRIBBINDINGPROC glVertexAttribBinding = nullptr;
+PFNGLVERTEXBINDINGDIVISORPROC glVertexBindingDivisor = nullptr;
+PFNGLDEBUGMESSAGECONTROLPROC glDebugMessageControl = nullptr;
+PFNGLDEBUGMESSAGEINSERTPROC glDebugMessageInsert = nullptr;
+PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback = nullptr;
+PFNGLGETDEBUGMESSAGELOGPROC glGetDebugMessageLog = nullptr;
+PFNGLPUSHDEBUGGROUPPROC glPushDebugGroup = nullptr;
+PFNGLPOPDEBUGGROUPPROC glPopDebugGroup = nullptr;
+PFNGLOBJECTLABELPROC glObjectLabel = nullptr;
+PFNGLGETOBJECTLABELPROC glGetObjectLabel = nullptr;
+PFNGLOBJECTPTRLABELPROC glObjectPtrLabel = nullptr;
+PFNGLGETOBJECTPTRLABELPROC glGetObjectPtrLabel = nullptr;
+
+// GL_VERSION_4_4
+PFNGLBUFFERSTORAGEPROC glBufferStorage = nullptr;
+PFNGLCLEARTEXIMAGEPROC glClearTexImage = nullptr;
+PFNGLCLEARTEXSUBIMAGEPROC glClearTexSubImage = nullptr;
+PFNGLBINDBUFFERSBASEPROC glBindBuffersBase = nullptr;
+PFNGLBINDBUFFERSRANGEPROC glBindBuffersRange = nullptr;
+PFNGLBINDTEXTURESPROC glBindTextures = nullptr;
+PFNGLBINDSAMPLERSPROC glBindSamplers = nullptr;
+PFNGLBINDIMAGETEXTURESPROC glBindImageTextures = nullptr;
+PFNGLBINDVERTEXBUFFERSPROC glBindVertexBuffers = nullptr;
+
+// GL_VERSION_4_5
+PFNGLCLIPCONTROLPROC glClipControl = nullptr;
+PFNGLCREATETRANSFORMFEEDBACKSPROC glCreateTransformFeedbacks = nullptr;
+PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glTransformFeedbackBufferBase = nullptr;
+PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glTransformFeedbackBufferRange = nullptr;
+PFNGLGETTRANSFORMFEEDBACKIVPROC glGetTransformFeedbackiv = nullptr;
+PFNGLGETTRANSFORMFEEDBACKI_VPROC glGetTransformFeedbacki_v = nullptr;
+PFNGLGETTRANSFORMFEEDBACKI64_VPROC glGetTransformFeedbacki64_v = nullptr;
+PFNGLCREATEBUFFERSPROC glCreateBuffers = nullptr;
+PFNGLNAMEDBUFFERSTORAGEPROC glNamedBufferStorage = nullptr;
+PFNGLNAMEDBUFFERDATAPROC glNamedBufferData = nullptr;
+PFNGLNAMEDBUFFERSUBDATAPROC glNamedBufferSubData = nullptr;
+PFNGLCOPYNAMEDBUFFERSUBDATAPROC glCopyNamedBufferSubData = nullptr;
+PFNGLCLEARNAMEDBUFFERDATAPROC glClearNamedBufferData = nullptr;
+PFNGLCLEARNAMEDBUFFERSUBDATAPROC glClearNamedBufferSubData = nullptr;
+PFNGLMAPNAMEDBUFFERPROC glMapNamedBuffer = nullptr;
+PFNGLMAPNAMEDBUFFERRANGEPROC glMapNamedBufferRange = nullptr;
+PFNGLUNMAPNAMEDBUFFERPROC glUnmapNamedBuffer = nullptr;
+PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glFlushMappedNamedBufferRange = nullptr;
+PFNGLGETNAMEDBUFFERPARAMETERIVPROC glGetNamedBufferParameteriv = nullptr;
+PFNGLGETNAMEDBUFFERPARAMETERI64VPROC glGetNamedBufferParameteri64v = nullptr;
+PFNGLGETNAMEDBUFFERPOINTERVPROC glGetNamedBufferPointerv = nullptr;
+PFNGLGETNAMEDBUFFERSUBDATAPROC glGetNamedBufferSubData = nullptr;
+PFNGLCREATEFRAMEBUFFERSPROC glCreateFramebuffers = nullptr;
+PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glNamedFramebufferRenderbuffer = nullptr;
+PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glNamedFramebufferParameteri = nullptr;
+PFNGLNAMEDFRAMEBUFFERTEXTUREPROC glNamedFramebufferTexture = nullptr;
+PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glNamedFramebufferTextureLayer = nullptr;
+PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glNamedFramebufferDrawBuffer = nullptr;
+PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glNamedFramebufferDrawBuffers = nullptr;
+PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glNamedFramebufferReadBuffer = nullptr;
+PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glInvalidateNamedFramebufferData = nullptr;
+PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glInvalidateNamedFramebufferSubData = nullptr;
+PFNGLCLEARNAMEDFRAMEBUFFERIVPROC glClearNamedFramebufferiv = nullptr;
+PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glClearNamedFramebufferuiv = nullptr;
+PFNGLCLEARNAMEDFRAMEBUFFERFVPROC glClearNamedFramebufferfv = nullptr;
+PFNGLCLEARNAMEDFRAMEBUFFERFIPROC glClearNamedFramebufferfi = nullptr;
+PFNGLBLITNAMEDFRAMEBUFFERPROC glBlitNamedFramebuffer = nullptr;
+PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glCheckNamedFramebufferStatus = nullptr;
+PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glGetNamedFramebufferParameteriv = nullptr;
+PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetNamedFramebufferAttachmentParameteriv = nullptr;
+PFNGLCREATERENDERBUFFERSPROC glCreateRenderbuffers = nullptr;
+PFNGLNAMEDRENDERBUFFERSTORAGEPROC glNamedRenderbufferStorage = nullptr;
+PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glNamedRenderbufferStorageMultisample = nullptr;
+PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glGetNamedRenderbufferParameteriv = nullptr;
+PFNGLCREATETEXTURESPROC glCreateTextures = nullptr;
+PFNGLTEXTUREBUFFERPROC glTextureBuffer = nullptr;
+PFNGLTEXTUREBUFFERRANGEPROC glTextureBufferRange = nullptr;
+PFNGLTEXTURESTORAGE1DPROC glTextureStorage1D = nullptr;
+PFNGLTEXTURESTORAGE2DPROC glTextureStorage2D = nullptr;
+PFNGLTEXTURESTORAGE3DPROC glTextureStorage3D = nullptr;
+PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glTextureStorage2DMultisample = nullptr;
+PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glTextureStorage3DMultisample = nullptr;
+PFNGLTEXTURESUBIMAGE1DPROC glTextureSubImage1D = nullptr;
+PFNGLTEXTURESUBIMAGE2DPROC glTextureSubImage2D = nullptr;
+PFNGLTEXTURESUBIMAGE3DPROC glTextureSubImage3D = nullptr;
+PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glCompressedTextureSubImage1D = nullptr;
+PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glCompressedTextureSubImage2D = nullptr;
+PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glCompressedTextureSubImage3D = nullptr;
+PFNGLCOPYTEXTURESUBIMAGE1DPROC glCopyTextureSubImage1D = nullptr;
+PFNGLCOPYTEXTURESUBIMAGE2DPROC glCopyTextureSubImage2D = nullptr;
+PFNGLCOPYTEXTURESUBIMAGE3DPROC glCopyTextureSubImage3D = nullptr;
+PFNGLTEXTUREPARAMETERFPROC glTextureParameterf = nullptr;
+PFNGLTEXTUREPARAMETERFVPROC glTextureParameterfv = nullptr;
+PFNGLTEXTUREPARAMETERIPROC glTextureParameteri = nullptr;
+PFNGLTEXTUREPARAMETERIIVPROC glTextureParameterIiv = nullptr;
+PFNGLTEXTUREPARAMETERIUIVPROC glTextureParameterIuiv = nullptr;
+PFNGLTEXTUREPARAMETERIVPROC glTextureParameteriv = nullptr;
+PFNGLGENERATETEXTUREMIPMAPPROC glGenerateTextureMipmap = nullptr;
+PFNGLBINDTEXTUREUNITPROC glBindTextureUnit = nullptr;
+PFNGLGETTEXTUREIMAGEPROC glGetTextureImage = nullptr;
+PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glGetCompressedTextureImage = nullptr;
+PFNGLGETTEXTURELEVELPARAMETERFVPROC glGetTextureLevelParameterfv = nullptr;
+PFNGLGETTEXTURELEVELPARAMETERIVPROC glGetTextureLevelParameteriv = nullptr;
+PFNGLGETTEXTUREPARAMETERFVPROC glGetTextureParameterfv = nullptr;
+PFNGLGETTEXTUREPARAMETERIIVPROC glGetTextureParameterIiv = nullptr;
+PFNGLGETTEXTUREPARAMETERIUIVPROC glGetTextureParameterIuiv = nullptr;
+PFNGLGETTEXTUREPARAMETERIVPROC glGetTextureParameteriv = nullptr;
+PFNGLCREATEVERTEXARRAYSPROC glCreateVertexArrays = nullptr;
+PFNGLDISABLEVERTEXARRAYATTRIBPROC glDisableVertexArrayAttrib = nullptr;
+PFNGLENABLEVERTEXARRAYATTRIBPROC glEnableVertexArrayAttrib = nullptr;
+PFNGLVERTEXARRAYELEMENTBUFFERPROC glVertexArrayElementBuffer = nullptr;
+PFNGLVERTEXARRAYVERTEXBUFFERPROC glVertexArrayVertexBuffer = nullptr;
+PFNGLVERTEXARRAYVERTEXBUFFERSPROC glVertexArrayVertexBuffers = nullptr;
+PFNGLVERTEXARRAYATTRIBBINDINGPROC glVertexArrayAttribBinding = nullptr;
+PFNGLVERTEXARRAYATTRIBFORMATPROC glVertexArrayAttribFormat = nullptr;
+PFNGLVERTEXARRAYATTRIBIFORMATPROC glVertexArrayAttribIFormat = nullptr;
+PFNGLVERTEXARRAYATTRIBLFORMATPROC glVertexArrayAttribLFormat = nullptr;
+PFNGLVERTEXARRAYBINDINGDIVISORPROC glVertexArrayBindingDivisor = nullptr;
+PFNGLGETVERTEXARRAYIVPROC glGetVertexArrayiv = nullptr;
+PFNGLGETVERTEXARRAYINDEXEDIVPROC glGetVertexArrayIndexediv = nullptr;
+PFNGLGETVERTEXARRAYINDEXED64IVPROC glGetVertexArrayIndexed64iv = nullptr;
+PFNGLCREATESAMPLERSPROC glCreateSamplers = nullptr;
+PFNGLCREATEPROGRAMPIPELINESPROC glCreateProgramPipelines = nullptr;
+PFNGLCREATEQUERIESPROC glCreateQueries = nullptr;
+PFNGLGETQUERYBUFFEROBJECTI64VPROC glGetQueryBufferObjecti64v = nullptr;
+PFNGLGETQUERYBUFFEROBJECTIVPROC glGetQueryBufferObjectiv = nullptr;
+PFNGLGETQUERYBUFFEROBJECTUI64VPROC glGetQueryBufferObjectui64v = nullptr;
+PFNGLGETQUERYBUFFEROBJECTUIVPROC glGetQueryBufferObjectuiv = nullptr;
+PFNGLMEMORYBARRIERBYREGIONPROC glMemoryBarrierByRegion = nullptr;
+PFNGLGETTEXTURESUBIMAGEPROC glGetTextureSubImage = nullptr;
+PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glGetCompressedTextureSubImage = nullptr;
+PFNGLGETGRAPHICSRESETSTATUSPROC glGetGraphicsResetStatus = nullptr;
+PFNGLGETNCOMPRESSEDTEXIMAGEPROC glGetnCompressedTexImage = nullptr;
+PFNGLGETNTEXIMAGEPROC glGetnTexImage = nullptr;
+PFNGLGETNUNIFORMDVPROC glGetnUniformdv = nullptr;
+PFNGLGETNUNIFORMFVPROC glGetnUniformfv = nullptr;
+PFNGLGETNUNIFORMIVPROC glGetnUniformiv = nullptr;
+PFNGLGETNUNIFORMUIVPROC glGetnUniformuiv = nullptr;
+PFNGLREADNPIXELSPROC glReadnPixels = nullptr;
+PFNGLGETNMAPDVPROC glGetnMapdv = nullptr;
+PFNGLGETNMAPFVPROC glGetnMapfv = nullptr;
+PFNGLGETNMAPIVPROC glGetnMapiv = nullptr;
+PFNGLGETNPIXELMAPFVPROC glGetnPixelMapfv = nullptr;
+PFNGLGETNPIXELMAPUIVPROC glGetnPixelMapuiv = nullptr;
+PFNGLGETNPIXELMAPUSVPROC glGetnPixelMapusv = nullptr;
+PFNGLGETNPOLYGONSTIPPLEPROC glGetnPolygonStipple = nullptr;
+PFNGLGETNCOLORTABLEPROC glGetnColorTable = nullptr;
+PFNGLGETNCONVOLUTIONFILTERPROC glGetnConvolutionFilter = nullptr;
+PFNGLGETNSEPARABLEFILTERPROC glGetnSeparableFilter = nullptr;
+PFNGLGETNHISTOGRAMPROC glGetnHistogram = nullptr;
+PFNGLGETNMINMAXPROC glGetnMinmax = nullptr;
+PFNGLTEXTUREBARRIERPROC glTextureBarrier = nullptr;
+
+// GL_VERSION_4_6
+PFNGLSPECIALIZESHADERPROC glSpecializeShader = nullptr;
+PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC glMultiDrawArraysIndirectCount = nullptr;
+PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC glMultiDrawElementsIndirectCount = nullptr;
+PFNGLPOLYGONOFFSETCLAMPPROC glPolygonOffsetClamp = nullptr;
+
+#endif
+
+LLGLManager gGLManager;
+
+LLGLManager::LLGLManager() :
+ mInited(false),
+ mIsDisabled(false),
+ mMaxSamples(0),
+ mNumTextureImageUnits(1),
+ mMaxSampleMaskWords(0),
+ mMaxColorTextureSamples(0),
+ mMaxDepthTextureSamples(0),
+ mMaxIntegerSamples(0),
+ mIsAMD(false),
+ mIsNVIDIA(false),
+ mIsIntel(false),
+#if LL_DARWIN
+ mIsMobileGF(false),
+#endif
+ mHasRequirements(true),
+ mDriverVersionMajor(1),
+ mDriverVersionMinor(0),
+ mDriverVersionRelease(0),
+ mGLVersion(1.0f),
+ mGLSLVersionMajor(0),
+ mGLSLVersionMinor(0),
+ mVRAM(0),
+ mGLMaxVertexRange(0),
+ mGLMaxIndexRange(0)
+{
+}
+
+//---------------------------------------------------------------------
+// Global initialization for GL
+//---------------------------------------------------------------------
+void LLGLManager::initWGL()
+{
+#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_ARB_create_context",gGLHExts.mSysExts))
+ {
+ GLH_EXT_NAME(wglCreateContextAttribsARB) = (PFNWGLCREATECONTEXTATTRIBSARBPROC)GLH_EXT_GET_PROC_ADDRESS("wglCreateContextAttribsARB");
+ }
+ else
+ {
+ LL_WARNS("RenderInit") << "No ARB create context extensions" << LL_ENDL;
+ }
+
+ // For retreiving information per AMD adapter,
+ // because we can't trust curently selected/default one when there are multiple
+ mHasAMDAssociations = ExtensionExists("WGL_AMD_gpu_association", gGLHExts.mSysExts);
+ if (mHasAMDAssociations)
+ {
+ GLH_EXT_NAME(wglGetGPUIDsAMD) = (PFNWGLGETGPUIDSAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetGPUIDsAMD");
+ GLH_EXT_NAME(wglGetGPUInfoAMD) = (PFNWGLGETGPUINFOAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetGPUInfoAMD");
+ }
+
+ 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;
+ }
+#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;
+ }
+
+#if 0 && LL_WINDOWS
+ if (!glGetStringi)
+ {
+ glGetStringi = (PFNGLGETSTRINGIPROC) GLH_EXT_GET_PROC_ADDRESS("glGetStringi");
+ }
+
+ //reload extensions string (may have changed after using wglCreateContextAttrib)
+ if (glGetStringi)
+ {
+ std::stringstream str;
+
+ GLint count = 0;
+ glGetIntegerv(GL_NUM_EXTENSIONS, &count);
+ for (GLint i = 0; i < count; ++i)
+ {
+ std::string ext = ll_safe_string((const char*) glGetStringi(GL_EXTENSIONS, i));
+ str << ext << " ";
+ LL_DEBUGS("GLExtensions") << ext << LL_ENDL;
+ }
+
+ {
+ PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = 0;
+ wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
+ if(wglGetExtensionsStringARB)
+ {
+ str << (const char*) wglGetExtensionsStringARB(wglGetCurrentDC());
+ }
+ }
+
+ free(gGLHExts.mSysExts);
+ std::string extensions = str.str();
+ gGLHExts.mSysExts = strdup(extensions.c_str());
+ }
+#endif
+
+ // Extract video card strings and convert to upper case to
+ // work around driver-to-driver variation in capitalization.
+ mGLVendor = ll_safe_string((const char *)glGetString(GL_VENDOR));
+ LLStringUtil::toUpper(mGLVendor);
+
+ mGLRenderer = ll_safe_string((const char *)glGetString(GL_RENDERER));
+ LLStringUtil::toUpper(mGLRenderer);
+
+ parse_gl_version( &mDriverVersionMajor,
+ &mDriverVersionMinor,
+ &mDriverVersionRelease,
+ &mDriverVersionVendorString,
+ &mGLVersionString);
+
+ mGLVersion = mDriverVersionMajor + mDriverVersionMinor * .1f;
+
+ if (mGLVersion >= 2.f)
+ {
+ parse_glsl_version(mGLSLVersionMajor, mGLSLVersionMinor);
+
+#if 0 && LL_DARWIN
+ // TODO maybe switch to using a core profile for GL 3.2?
+ // https://stackoverflow.com/a/19868861
+ //never use GLSL greater than 1.20 on OSX
+ if (mGLSLVersionMajor > 1 || mGLSLVersionMinor > 30)
+ {
+ mGLSLVersionMajor = 1;
+ mGLSLVersionMinor = 30;
+ }
+#endif
+ }
+
+ if (mGLVersion >= 2.1f && LLImageGL::sCompressTextures)
+ { //use texture compression
+ glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST);
+ }
+ else
+ { //GL version is < 3.0, always disable texture compression
+ LLImageGL::sCompressTextures = false;
+ }
+
+ // Trailing space necessary to keep "nVidia Corpor_ati_on" cards
+ // from being recognized as ATI.
+ // NOTE: AMD has been pretty good about not breaking this check, do not rename without good reason
+ if (mGLVendor.substr(0,4) == "ATI ")
+ {
+ mGLVendorShort = "AMD";
+ // *TODO: Fix this?
+ mIsAMD = true;
+ }
+ else if (mGLVendor.find("NVIDIA ") != std::string::npos)
+ {
+ mGLVendorShort = "NVIDIA";
+ mIsNVIDIA = 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();
+
+ S32 old_vram = mVRAM;
+ mVRAM = 0;
+
+#if LL_WINDOWS
+ if (mHasAMDAssociations)
+ {
+ GLuint gl_gpus_count = wglGetGPUIDsAMD(0, 0);
+ if (gl_gpus_count > 0)
+ {
+ GLuint* ids = new GLuint[gl_gpus_count];
+ wglGetGPUIDsAMD(gl_gpus_count, ids);
+
+ GLuint mem_mb = 0;
+ for (U32 i = 0; i < gl_gpus_count; i++)
+ {
+ wglGetGPUInfoAMD(ids[i],
+ WGL_GPU_RAM_AMD,
+ GL_UNSIGNED_INT,
+ sizeof(GLuint),
+ &mem_mb);
+ if (mVRAM < mem_mb)
+ {
+ // basically pick the best AMD and trust driver/OS to know to switch
+ mVRAM = mem_mb;
+ }
+ }
+ }
+ if (mVRAM != 0)
+ {
+ LL_WARNS("RenderInit") << "VRAM Detected (AMDAssociations):" << mVRAM << LL_ENDL;
+ }
+ }
+#endif
+
+#if LL_WINDOWS
+ if (mVRAM < 256)
+ {
+ // Something likely went wrong using the above extensions
+ // try WMI first and fall back to old method (from dxdiag) if all else fails
+ // Function will check all GPUs WMI knows of and will pick up the one with most
+ // memory. We need to check all GPUs because system can switch active GPU to
+ // weaker one, to preserve power when not under load.
+ S32 mem = LLDXHardware::getMBVideoMemoryViaWMI();
+ if (mem != 0)
+ {
+ mVRAM = mem;
+ LL_WARNS("RenderInit") << "VRAM Detected (WMI):" << mVRAM<< LL_ENDL;
+ }
+ }
+#endif
+
+ if (mVRAM < 256 && old_vram > 0)
+ {
+ // fall back to old method
+ // Note: on Windows value will be from LLDXHardware.
+ // Either received via dxdiag or via WMI by id from dxdiag.
+ mVRAM = old_vram;
+ }
+
+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mNumTextureImageUnits);
+ glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &mMaxColorTextureSamples);
+ glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &mMaxDepthTextureSamples);
+ glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &mMaxIntegerSamples);
+ glGetIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &mMaxSampleMaskWords);
+ glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples);
+
+ if (mGLVersion >= 4.59f)
+ {
+ glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &mMaxAnisotropy);
+ }
+
+ initGLStates();
+
+ return true;
+}
+
+void LLGLManager::getGLInfo(LLSD& info)
+{
+ if (gHeadlessClient)
+ {
+ info["GLInfo"]["GLVendor"] = HEADLESS_VENDOR_STRING;
+ info["GLInfo"]["GLRenderer"] = HEADLESS_RENDERER_STRING;
+ info["GLInfo"]["GLVersion"] = HEADLESS_VERSION_STRING;
+ return;
+ }
+ else
+ {
+ info["GLInfo"]["GLVendor"] = ll_safe_string((const char *)glGetString(GL_VENDOR));
+ info["GLInfo"]["GLRenderer"] = ll_safe_string((const char *)glGetString(GL_RENDERER));
+ info["GLInfo"]["GLVersion"] = ll_safe_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;
+
+ if (gHeadlessClient)
+ {
+ info_str += std::string("GL_VENDOR ") + HEADLESS_VENDOR_STRING + std::string("\n");
+ info_str += std::string("GL_RENDERER ") + HEADLESS_RENDERER_STRING + std::string("\n");
+ info_str += std::string("GL_VERSION ") + HEADLESS_VERSION_STRING + std::string("\n");
+ }
+ else
+ {
+ 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
+ std::string all_exts= ll_safe_string(((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()
+{
+ if (gHeadlessClient)
+ {
+ LL_INFOS("RenderInit") << "GL_VENDOR: " << HEADLESS_VENDOR_STRING << LL_ENDL;
+ LL_INFOS("RenderInit") << "GL_RENDERER: " << HEADLESS_RENDERER_STRING << LL_ENDL;
+ LL_INFOS("RenderInit") << "GL_VERSION: " << HEADLESS_VERSION_STRING << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS("RenderInit") << "GL_VENDOR: " << ll_safe_string((const char *)glGetString(GL_VENDOR)) << LL_ENDL;
+ LL_INFOS("RenderInit") << "GL_RENDERER: " << ll_safe_string((const char *)glGetString(GL_RENDERER)) << LL_ENDL;
+ LL_INFOS("RenderInit") << "GL_VERSION: " << ll_safe_string((const char *)glGetString(GL_VERSION)) << LL_ENDL;
+ }
+
+#if !LL_MESA_HEADLESS
+ std::string all_exts= ll_safe_string(((const char *)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;
+ if (gHeadlessClient)
+ {
+ gl_string = HEADLESS_VENDOR_STRING + " " + HEADLESS_RENDERER_STRING;
+ }
+ else
+ {
+ gl_string = ll_safe_string((char*)glGetString(GL_VENDOR)) + " " + ll_safe_string((char*)glGetString(GL_RENDERER));
+ }
+ return gl_string;
+}
+
+void LLGLManager::asLLSD(LLSD& info)
+{
+ // Currently these are duplicates of fields in "system".
+ info["gpu_vendor"] = mGLVendorShort;
+ info["gpu_version"] = mDriverVersionVendorString;
+ info["opengl_version"] = mGLVersionString;
+
+ info["vram"] = mVRAM;
+
+ // OpenGL limits
+ info["max_samples"] = mMaxSamples;
+ info["num_texture_image_units"] = mNumTextureImageUnits;
+ info["max_sample_mask_words"] = mMaxSampleMaskWords;
+ info["max_color_texture_samples"] = mMaxColorTextureSamples;
+ info["max_depth_texture_samples"] = mMaxDepthTextureSamples;
+ info["max_integer_samples"] = mMaxIntegerSamples;
+ info["max_vertex_range"] = mGLMaxVertexRange;
+ info["max_index_range"] = mGLMaxIndexRange;
+ info["max_texture_size"] = mGLMaxTextureSize;
+
+ // Which vendor
+ info["is_ati"] = mIsAMD; // note, do not rename is_ati to is_amd without coordinating with DW
+ info["is_nvidia"] = mIsNVIDIA;
+ info["is_intel"] = mIsIntel;
+
+ info["gl_renderer"] = mGLRenderer;
+}
+
+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_DARWIN
+ GLint num_extensions = 0;
+ std::string all_extensions{""};
+ glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
+ for(GLint i = 0; i < num_extensions; ++i) {
+ char const * extension = (char const *)glGetStringi(GL_EXTENSIONS, i);
+ all_extensions += extension;
+ all_extensions += ' ';
+ }
+ if (num_extensions)
+ {
+ all_extensions += "GL_ARB_multitexture GL_ARB_texture_cube_map GL_ARB_texture_compression "; // These are in 3.2 core, but not listed by OSX
+ gGLHExts.mSysExts = strdup(all_extensions.data());
+ }
+#endif
+
+ // NOTE: version checks against mGLVersion should bias down by 0.01 because of F32 errors
+
+ // OpenGL 4.x capabilities
+ mHasCubeMapArray = mGLVersion >= 3.99f;
+ mHasTransformFeedback = mGLVersion >= 3.99f;
+ mHasDebugOutput = mGLVersion >= 4.29f;
+
+ // Misc
+ glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, (GLint*) &mGLMaxVertexRange);
+ glGetIntegerv(GL_MAX_ELEMENTS_INDICES, (GLint*) &mGLMaxIndexRange);
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint*) &mGLMaxTextureSize);
+
+ mInited = true;
+
+#if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS
+ LL_DEBUGS("RenderInit") << "GL Probe: Getting symbols" << LL_ENDL;
+
+#if LL_WINDOWS
+ // WGL_AMD_gpu_association
+ wglGetGPUIDsAMD = (PFNWGLGETGPUIDSAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetGPUIDsAMD");
+ wglGetGPUInfoAMD = (PFNWGLGETGPUINFOAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetGPUInfoAMD");
+ wglGetContextGPUIDAMD = (PFNWGLGETCONTEXTGPUIDAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetContextGPUIDAMD");
+ wglCreateAssociatedContextAMD = (PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglCreateAssociatedContextAMD");
+ wglCreateAssociatedContextAttribsAMD = (PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglCreateAssociatedContextAttribsAMD");
+ wglDeleteAssociatedContextAMD = (PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglDeleteAssociatedContextAMD");
+ wglMakeAssociatedContextCurrentAMD = (PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglMakeAssociatedContextCurrentAMD");
+ wglGetCurrentAssociatedContextAMD = (PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetCurrentAssociatedContextAMD");
+ wglBlitContextFramebufferAMD = (PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglBlitContextFramebufferAMD");
+
+ // WGL_EXT_swap_control
+ wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)GLH_EXT_GET_PROC_ADDRESS("wglSwapIntervalEXT");
+ wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetSwapIntervalEXT");
+
+ // WGL_ARB_create_context
+ wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)GLH_EXT_GET_PROC_ADDRESS("wglCreateContextAttribsARB");
+#endif
+
+
+ // Load entire OpenGL API through GetProcAddress, leaving sections beyond mGLVersion unloaded
+
+ // GL_VERSION_1_2
+ if (mGLVersion < 1.19f)
+ {
+ return;
+ }
+ glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawRangeElements");
+ glTexImage3D = (PFNGLTEXIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glTexImage3D");
+ glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glTexSubImage3D");
+ glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyTexSubImage3D");
+
+
+ // GL_VERSION_1_3
+ if (mGLVersion < 1.29f)
+ {
+ return;
+ }
+ glActiveTexture = (PFNGLACTIVETEXTUREPROC)GLH_EXT_GET_PROC_ADDRESS("glActiveTexture");
+ glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glSampleCoverage");
+ glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTexImage3D");
+ glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTexImage2D");
+ glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTexImage1D");
+ glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTexSubImage3D");
+ glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTexSubImage2D");
+ glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTexSubImage1D");
+ glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetCompressedTexImage");
+ glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC)GLH_EXT_GET_PROC_ADDRESS("glClientActiveTexture");
+ glMultiTexCoord1d = (PFNGLMULTITEXCOORD1DPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1d");
+ glMultiTexCoord1dv = (PFNGLMULTITEXCOORD1DVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1dv");
+ glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1f");
+ glMultiTexCoord1fv = (PFNGLMULTITEXCOORD1FVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1fv");
+ glMultiTexCoord1i = (PFNGLMULTITEXCOORD1IPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1i");
+ glMultiTexCoord1iv = (PFNGLMULTITEXCOORD1IVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1iv");
+ glMultiTexCoord1s = (PFNGLMULTITEXCOORD1SPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1s");
+ glMultiTexCoord1sv = (PFNGLMULTITEXCOORD1SVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord1sv");
+ glMultiTexCoord2d = (PFNGLMULTITEXCOORD2DPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2d");
+ glMultiTexCoord2dv = (PFNGLMULTITEXCOORD2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2dv");
+ glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2f");
+ glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2fv");
+ glMultiTexCoord2i = (PFNGLMULTITEXCOORD2IPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2i");
+ glMultiTexCoord2iv = (PFNGLMULTITEXCOORD2IVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2iv");
+ glMultiTexCoord2s = (PFNGLMULTITEXCOORD2SPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2s");
+ glMultiTexCoord2sv = (PFNGLMULTITEXCOORD2SVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord2sv");
+ glMultiTexCoord3d = (PFNGLMULTITEXCOORD3DPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3d");
+ glMultiTexCoord3dv = (PFNGLMULTITEXCOORD3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3dv");
+ glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3f");
+ glMultiTexCoord3fv = (PFNGLMULTITEXCOORD3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3fv");
+ glMultiTexCoord3i = (PFNGLMULTITEXCOORD3IPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3i");
+ glMultiTexCoord3iv = (PFNGLMULTITEXCOORD3IVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3iv");
+ glMultiTexCoord3s = (PFNGLMULTITEXCOORD3SPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3s");
+ glMultiTexCoord3sv = (PFNGLMULTITEXCOORD3SVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord3sv");
+ glMultiTexCoord4d = (PFNGLMULTITEXCOORD4DPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4d");
+ glMultiTexCoord4dv = (PFNGLMULTITEXCOORD4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4dv");
+ glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4f");
+ glMultiTexCoord4fv = (PFNGLMULTITEXCOORD4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4fv");
+ glMultiTexCoord4i = (PFNGLMULTITEXCOORD4IPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4i");
+ glMultiTexCoord4iv = (PFNGLMULTITEXCOORD4IVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4iv");
+ glMultiTexCoord4s = (PFNGLMULTITEXCOORD4SPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4s");
+ glMultiTexCoord4sv = (PFNGLMULTITEXCOORD4SVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoord4sv");
+ glLoadTransposeMatrixf = (PFNGLLOADTRANSPOSEMATRIXFPROC)GLH_EXT_GET_PROC_ADDRESS("glLoadTransposeMatrixf");
+ glLoadTransposeMatrixd = (PFNGLLOADTRANSPOSEMATRIXDPROC)GLH_EXT_GET_PROC_ADDRESS("glLoadTransposeMatrixd");
+ glMultTransposeMatrixf = (PFNGLMULTTRANSPOSEMATRIXFPROC)GLH_EXT_GET_PROC_ADDRESS("glMultTransposeMatrixf");
+ glMultTransposeMatrixd = (PFNGLMULTTRANSPOSEMATRIXDPROC)GLH_EXT_GET_PROC_ADDRESS("glMultTransposeMatrixd");
+
+ // GL_VERSION_1_4
+ if (mGLVersion < 1.39f)
+ {
+ return;
+ }
+ glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendFuncSeparate");
+ glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawArrays");
+ glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawElements");
+ glPointParameterf = (PFNGLPOINTPARAMETERFPROC)GLH_EXT_GET_PROC_ADDRESS("glPointParameterf");
+ glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glPointParameterfv");
+ glPointParameteri = (PFNGLPOINTPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glPointParameteri");
+ glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glPointParameteriv");
+ glFogCoordf = (PFNGLFOGCOORDFPROC)GLH_EXT_GET_PROC_ADDRESS("glFogCoordf");
+ glFogCoordfv = (PFNGLFOGCOORDFVPROC)GLH_EXT_GET_PROC_ADDRESS("glFogCoordfv");
+ glFogCoordd = (PFNGLFOGCOORDDPROC)GLH_EXT_GET_PROC_ADDRESS("glFogCoordd");
+ glFogCoorddv = (PFNGLFOGCOORDDVPROC)GLH_EXT_GET_PROC_ADDRESS("glFogCoorddv");
+ glFogCoordPointer = (PFNGLFOGCOORDPOINTERPROC)GLH_EXT_GET_PROC_ADDRESS("glFogCoordPointer");
+ glSecondaryColor3b = (PFNGLSECONDARYCOLOR3BPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3b");
+ glSecondaryColor3bv = (PFNGLSECONDARYCOLOR3BVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3bv");
+ glSecondaryColor3d = (PFNGLSECONDARYCOLOR3DPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3d");
+ glSecondaryColor3dv = (PFNGLSECONDARYCOLOR3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3dv");
+ glSecondaryColor3f = (PFNGLSECONDARYCOLOR3FPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3f");
+ glSecondaryColor3fv = (PFNGLSECONDARYCOLOR3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3fv");
+ glSecondaryColor3i = (PFNGLSECONDARYCOLOR3IPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3i");
+ glSecondaryColor3iv = (PFNGLSECONDARYCOLOR3IVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3iv");
+ glSecondaryColor3s = (PFNGLSECONDARYCOLOR3SPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3s");
+ glSecondaryColor3sv = (PFNGLSECONDARYCOLOR3SVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3sv");
+ glSecondaryColor3ub = (PFNGLSECONDARYCOLOR3UBPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3ub");
+ glSecondaryColor3ubv = (PFNGLSECONDARYCOLOR3UBVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3ubv");
+ glSecondaryColor3ui = (PFNGLSECONDARYCOLOR3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3ui");
+ glSecondaryColor3uiv = (PFNGLSECONDARYCOLOR3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3uiv");
+ glSecondaryColor3us = (PFNGLSECONDARYCOLOR3USPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3us");
+ glSecondaryColor3usv = (PFNGLSECONDARYCOLOR3USVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColor3usv");
+ glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColorPointer");
+ glWindowPos2d = (PFNGLWINDOWPOS2DPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2d");
+ glWindowPos2dv = (PFNGLWINDOWPOS2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2dv");
+ glWindowPos2f = (PFNGLWINDOWPOS2FPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2f");
+ glWindowPos2fv = (PFNGLWINDOWPOS2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2fv");
+ glWindowPos2i = (PFNGLWINDOWPOS2IPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2i");
+ glWindowPos2iv = (PFNGLWINDOWPOS2IVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2iv");
+ glWindowPos2s = (PFNGLWINDOWPOS2SPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2s");
+ glWindowPos2sv = (PFNGLWINDOWPOS2SVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos2sv");
+ glWindowPos3d = (PFNGLWINDOWPOS3DPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3d");
+ glWindowPos3dv = (PFNGLWINDOWPOS3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3dv");
+ glWindowPos3f = (PFNGLWINDOWPOS3FPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3f");
+ glWindowPos3fv = (PFNGLWINDOWPOS3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3fv");
+ glWindowPos3i = (PFNGLWINDOWPOS3IPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3i");
+ glWindowPos3iv = (PFNGLWINDOWPOS3IVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3iv");
+ glWindowPos3s = (PFNGLWINDOWPOS3SPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3s");
+ glWindowPos3sv = (PFNGLWINDOWPOS3SVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3sv");
+
+ // GL_VERSION_1_5
+ if (mGLVersion < 1.49f)
+ {
+ return;
+ }
+ glGenQueries = (PFNGLGENQUERIESPROC)GLH_EXT_GET_PROC_ADDRESS("glGenQueries");
+ glDeleteQueries = (PFNGLDELETEQUERIESPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteQueries");
+ glIsQuery = (PFNGLISQUERYPROC)GLH_EXT_GET_PROC_ADDRESS("glIsQuery");
+ glBeginQuery = (PFNGLBEGINQUERYPROC)GLH_EXT_GET_PROC_ADDRESS("glBeginQuery");
+ glEndQuery = (PFNGLENDQUERYPROC)GLH_EXT_GET_PROC_ADDRESS("glEndQuery");
+ glGetQueryiv = (PFNGLGETQUERYIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryiv");
+ glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryObjectiv");
+ glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryObjectuiv");
+ glBindBuffer = (PFNGLBINDBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glBindBuffer");
+ glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteBuffers");
+ glGenBuffers = (PFNGLGENBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glGenBuffers");
+ glIsBuffer = (PFNGLISBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glIsBuffer");
+ glBufferData = (PFNGLBUFFERDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glBufferData");
+ glBufferSubData = (PFNGLBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glBufferSubData");
+ glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferSubData");
+ glMapBuffer = (PFNGLMAPBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glMapBuffer");
+ glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glUnmapBuffer");
+ glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferParameteriv");
+ glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferPointerv");
+
+ // GL_VERSION_2_0
+ if (mGLVersion < 1.9f)
+ {
+ return;
+ }
+ glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendEquationSeparate");
+ glDrawBuffers = (PFNGLDRAWBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawBuffers");
+ glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)GLH_EXT_GET_PROC_ADDRESS("glStencilOpSeparate");
+ glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)GLH_EXT_GET_PROC_ADDRESS("glStencilFuncSeparate");
+ glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)GLH_EXT_GET_PROC_ADDRESS("glStencilMaskSeparate");
+ glAttachShader = (PFNGLATTACHSHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glAttachShader");
+ glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glBindAttribLocation");
+ glCompileShader = (PFNGLCOMPILESHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glCompileShader");
+ glCreateProgram = (PFNGLCREATEPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateProgram");
+ glCreateShader = (PFNGLCREATESHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateShader");
+ glDeleteProgram = (PFNGLDELETEPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteProgram");
+ glDeleteShader = (PFNGLDELETESHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteShader");
+ glDetachShader = (PFNGLDETACHSHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glDetachShader");
+ glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)GLH_EXT_GET_PROC_ADDRESS("glDisableVertexAttribArray");
+ glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)GLH_EXT_GET_PROC_ADDRESS("glEnableVertexAttribArray");
+ glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveAttrib");
+ glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniform");
+ glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)GLH_EXT_GET_PROC_ADDRESS("glGetAttachedShaders");
+ glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glGetAttribLocation");
+ glGetProgramiv = (PFNGLGETPROGRAMIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramiv");
+ glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramInfoLog");
+ glGetShaderiv = (PFNGLGETSHADERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetShaderiv");
+ glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)GLH_EXT_GET_PROC_ADDRESS("glGetShaderInfoLog");
+ glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetShaderSource");
+ glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformLocation");
+ glGetUniformfv = (PFNGLGETUNIFORMFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformfv");
+ glGetUniformiv = (PFNGLGETUNIFORMIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformiv");
+ glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribdv");
+ glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribfv");
+ glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribiv");
+ glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribPointerv");
+ glIsProgram = (PFNGLISPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glIsProgram");
+ glIsShader = (PFNGLISSHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glIsShader");
+ glLinkProgram = (PFNGLLINKPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glLinkProgram");
+ glShaderSource = (PFNGLSHADERSOURCEPROC)GLH_EXT_GET_PROC_ADDRESS("glShaderSource");
+ glUseProgram = (PFNGLUSEPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glUseProgram");
+ glUniform1f = (PFNGLUNIFORM1FPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1f");
+ glUniform2f = (PFNGLUNIFORM2FPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2f");
+ glUniform3f = (PFNGLUNIFORM3FPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3f");
+ glUniform4f = (PFNGLUNIFORM4FPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4f");
+ glUniform1i = (PFNGLUNIFORM1IPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1i");
+ glUniform2i = (PFNGLUNIFORM2IPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2i");
+ glUniform3i = (PFNGLUNIFORM3IPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3i");
+ glUniform4i = (PFNGLUNIFORM4IPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4i");
+ glUniform1fv = (PFNGLUNIFORM1FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1fv");
+ glUniform2fv = (PFNGLUNIFORM2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2fv");
+ glUniform3fv = (PFNGLUNIFORM3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3fv");
+ glUniform4fv = (PFNGLUNIFORM4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4fv");
+ glUniform1iv = (PFNGLUNIFORM1IVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1iv");
+ glUniform2iv = (PFNGLUNIFORM2IVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2iv");
+ glUniform3iv = (PFNGLUNIFORM3IVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3iv");
+ glUniform4iv = (PFNGLUNIFORM4IVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4iv");
+ glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2fv");
+ glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3fv");
+ glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4fv");
+ glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glValidateProgram");
+ glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1d");
+ glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1dv");
+ glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1f");
+ glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1fv");
+ glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1s");
+ glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1sv");
+ glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2d");
+ glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2dv");
+ glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2f");
+ glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2fv");
+ glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2s");
+ glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2sv");
+ glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3d");
+ glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3dv");
+ glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3f");
+ glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3fv");
+ glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3s");
+ glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3sv");
+ glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Nbv");
+ glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Niv");
+ glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Nsv");
+ glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Nub");
+ glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Nubv");
+ glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Nuiv");
+ glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4Nusv");
+ glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4bv");
+ glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4d");
+ glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4dv");
+ glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4f");
+ glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4fv");
+ glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4iv");
+ glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4s");
+ glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4sv");
+ glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4ubv");
+ glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4uiv");
+ glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4usv");
+ glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribPointer");
+
+ // GL_VERSION_2_1
+ if (mGLVersion < 2.09f)
+ {
+ return;
+ }
+ glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2x3fv");
+ glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3x2fv");
+ glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2x4fv");
+ glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4x2fv");
+ glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3x4fv");
+ glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4x3fv");
+
+ // GL_VERSION_3_0
+ if (mGLVersion < 2.99f)
+ {
+ return;
+ }
+ glColorMaski = (PFNGLCOLORMASKIPROC)GLH_EXT_GET_PROC_ADDRESS("glColorMaski");
+ glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBooleani_v");
+ glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetIntegeri_v");
+ glEnablei = (PFNGLENABLEIPROC)GLH_EXT_GET_PROC_ADDRESS("glEnablei");
+ glDisablei = (PFNGLDISABLEIPROC)GLH_EXT_GET_PROC_ADDRESS("glDisablei");
+ glIsEnabledi = (PFNGLISENABLEDIPROC)GLH_EXT_GET_PROC_ADDRESS("glIsEnabledi");
+ glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glBeginTransformFeedback");
+ glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glEndTransformFeedback");
+ glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glBindBufferRange");
+ glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)GLH_EXT_GET_PROC_ADDRESS("glBindBufferBase");
+ glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)GLH_EXT_GET_PROC_ADDRESS("glTransformFeedbackVaryings");
+ glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTransformFeedbackVarying");
+ glClampColor = (PFNGLCLAMPCOLORPROC)GLH_EXT_GET_PROC_ADDRESS("glClampColor");
+ glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC)GLH_EXT_GET_PROC_ADDRESS("glBeginConditionalRender");
+ glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC)GLH_EXT_GET_PROC_ADDRESS("glEndConditionalRender");
+ glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribIPointer");
+ glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribIiv");
+ glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribIuiv");
+ glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI1i");
+ glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI2i");
+ glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI3i");
+ glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4i");
+ glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI1ui");
+ glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI2ui");
+ glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI3ui");
+ glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4ui");
+ glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI1iv");
+ glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI2iv");
+ glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI3iv");
+ glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4iv");
+ glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI1uiv");
+ glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI2uiv");
+ glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI3uiv");
+ glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4uiv");
+ glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4bv");
+ glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4sv");
+ glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4ubv");
+ glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribI4usv");
+ glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformuiv");
+ glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glBindFragDataLocation");
+ glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glGetFragDataLocation");
+ glUniform1ui = (PFNGLUNIFORM1UIPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1ui");
+ glUniform2ui = (PFNGLUNIFORM2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2ui");
+ glUniform3ui = (PFNGLUNIFORM3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3ui");
+ glUniform4ui = (PFNGLUNIFORM4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4ui");
+ glUniform1uiv = (PFNGLUNIFORM1UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1uiv");
+ glUniform2uiv = (PFNGLUNIFORM2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2uiv");
+ glUniform3uiv = (PFNGLUNIFORM3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3uiv");
+ glUniform4uiv = (PFNGLUNIFORM4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4uiv");
+ glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTexParameterIiv");
+ glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTexParameterIuiv");
+ glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTexParameterIiv");
+ glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTexParameterIuiv");
+ glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferiv");
+ glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferuiv");
+ glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferfv");
+ glClearBufferfi = (PFNGLCLEARBUFFERFIPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferfi");
+ glGetStringi = (PFNGLGETSTRINGIPROC)GLH_EXT_GET_PROC_ADDRESS("glGetStringi");
+ glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glIsRenderbuffer");
+ glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glBindRenderbuffer");
+ glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteRenderbuffers");
+ glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glGenRenderbuffers");
+ glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glRenderbufferStorage");
+ glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetRenderbufferParameteriv");
+ glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glIsFramebuffer");
+ glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glBindFramebuffer");
+ glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteFramebuffers");
+ glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glGenFramebuffers");
+ glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)GLH_EXT_GET_PROC_ADDRESS("glCheckFramebufferStatus");
+ glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture1D");
+ glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture2D");
+ glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture3D");
+ glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferRenderbuffer");
+ glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetFramebufferAttachmentParameteriv");
+ glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)GLH_EXT_GET_PROC_ADDRESS("glGenerateMipmap");
+ glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glBlitFramebuffer");
+ glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glRenderbufferStorageMultisample");
+ glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferTextureLayer");
+ glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glMapBufferRange");
+ glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glFlushMappedBufferRange");
+ glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)GLH_EXT_GET_PROC_ADDRESS("glBindVertexArray");
+ glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteVertexArrays");
+ glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)GLH_EXT_GET_PROC_ADDRESS("glGenVertexArrays");
+ glIsVertexArray = (PFNGLISVERTEXARRAYPROC)GLH_EXT_GET_PROC_ADDRESS("glIsVertexArray");
+
+ // GL_VERSION_3_1
+ if (mGLVersion < 3.09f)
+ {
+ return;
+ }
+ glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawArraysInstanced");
+ glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsInstanced");
+ glTexBuffer = (PFNGLTEXBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glTexBuffer");
+ glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)GLH_EXT_GET_PROC_ADDRESS("glPrimitiveRestartIndex");
+ glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyBufferSubData");
+ glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformIndices");
+ glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniformsiv");
+ glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniformName");
+ glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformBlockIndex");
+ glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniformBlockiv");
+ glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniformBlockName");
+ glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformBlockBinding");
+
+ // GL_VERSION_3_2
+ if (mGLVersion < 3.19f)
+ {
+ return;
+ }
+ glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsBaseVertex");
+ glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawRangeElementsBaseVertex");
+ glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsInstancedBaseVertex");
+ glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawElementsBaseVertex");
+ glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC)GLH_EXT_GET_PROC_ADDRESS("glProvokingVertex");
+ glFenceSync = (PFNGLFENCESYNCPROC)GLH_EXT_GET_PROC_ADDRESS("glFenceSync");
+ glIsSync = (PFNGLISSYNCPROC)GLH_EXT_GET_PROC_ADDRESS("glIsSync");
+ glDeleteSync = (PFNGLDELETESYNCPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteSync");
+ glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)GLH_EXT_GET_PROC_ADDRESS("glClientWaitSync");
+ glWaitSync = (PFNGLWAITSYNCPROC)GLH_EXT_GET_PROC_ADDRESS("glWaitSync");
+ glGetInteger64v = (PFNGLGETINTEGER64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetInteger64v");
+ glGetSynciv = (PFNGLGETSYNCIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSynciv");
+ glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetInteger64i_v");
+ glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferParameteri64v");
+ glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture");
+ glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glTexImage2DMultisample");
+ glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glTexImage3DMultisample");
+ glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetMultisamplefv");
+ glSampleMaski = (PFNGLSAMPLEMASKIPROC)GLH_EXT_GET_PROC_ADDRESS("glSampleMaski");
+
+ // GL_VERSION_3_3
+ if (mGLVersion < 3.29f)
+ {
+ return;
+ }
+ glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)GLH_EXT_GET_PROC_ADDRESS("glBindFragDataLocationIndexed");
+ glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC)GLH_EXT_GET_PROC_ADDRESS("glGetFragDataIndex");
+ glGenSamplers = (PFNGLGENSAMPLERSPROC)GLH_EXT_GET_PROC_ADDRESS("glGenSamplers");
+ glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteSamplers");
+ glIsSampler = (PFNGLISSAMPLERPROC)GLH_EXT_GET_PROC_ADDRESS("glIsSampler");
+ glBindSampler = (PFNGLBINDSAMPLERPROC)GLH_EXT_GET_PROC_ADDRESS("glBindSampler");
+ glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glSamplerParameteri");
+ glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glSamplerParameteriv");
+ glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)GLH_EXT_GET_PROC_ADDRESS("glSamplerParameterf");
+ glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glSamplerParameterfv");
+ glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glSamplerParameterIiv");
+ glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glSamplerParameterIuiv");
+ glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSamplerParameteriv");
+ glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSamplerParameterIiv");
+ glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSamplerParameterfv");
+ glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSamplerParameterIuiv");
+ glQueryCounter = (PFNGLQUERYCOUNTERPROC)GLH_EXT_GET_PROC_ADDRESS("glQueryCounter");
+ glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryObjecti64v");
+ glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryObjectui64v");
+ glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribDivisor");
+ glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP1ui");
+ glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP1uiv");
+ glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP2ui");
+ glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP2uiv");
+ glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP3ui");
+ glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP3uiv");
+ glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP4ui");
+ glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribP4uiv");
+ glVertexP2ui = (PFNGLVERTEXP2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexP2ui");
+ glVertexP2uiv = (PFNGLVERTEXP2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexP2uiv");
+ glVertexP3ui = (PFNGLVERTEXP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexP3ui");
+ glVertexP3uiv = (PFNGLVERTEXP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexP3uiv");
+ glVertexP4ui = (PFNGLVERTEXP4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexP4ui");
+ glVertexP4uiv = (PFNGLVERTEXP4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexP4uiv");
+ glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP1ui");
+ glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP1uiv");
+ glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP2ui");
+ glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP2uiv");
+ glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP3ui");
+ glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP3uiv");
+ glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP4ui");
+ glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTexCoordP4uiv");
+ glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP1ui");
+ glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP1uiv");
+ glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP2ui");
+ glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP2uiv");
+ glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP3ui");
+ glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP3uiv");
+ glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP4ui");
+ glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiTexCoordP4uiv");
+ glNormalP3ui = (PFNGLNORMALP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glNormalP3ui");
+ glNormalP3uiv = (PFNGLNORMALP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glNormalP3uiv");
+ glColorP3ui = (PFNGLCOLORP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glColorP3ui");
+ glColorP3uiv = (PFNGLCOLORP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glColorP3uiv");
+ glColorP4ui = (PFNGLCOLORP4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glColorP4ui");
+ glColorP4uiv = (PFNGLCOLORP4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glColorP4uiv");
+ glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColorP3ui");
+ glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColorP3uiv");
+
+ // GL_VERSION_4_0
+ if (mGLVersion < 3.99f)
+ {
+ return;
+ }
+ glMinSampleShading = (PFNGLMINSAMPLESHADINGPROC)GLH_EXT_GET_PROC_ADDRESS("glMinSampleShading");
+ glBlendEquationi = (PFNGLBLENDEQUATIONIPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendEquationi");
+ glBlendEquationSeparatei = (PFNGLBLENDEQUATIONSEPARATEIPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendEquationSeparatei");
+ glBlendFunci = (PFNGLBLENDFUNCIPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendFunci");
+ glBlendFuncSeparatei = (PFNGLBLENDFUNCSEPARATEIPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendFuncSeparatei");
+ glDrawArraysIndirect = (PFNGLDRAWARRAYSINDIRECTPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawArraysIndirect");
+ glDrawElementsIndirect = (PFNGLDRAWELEMENTSINDIRECTPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsIndirect");
+ glUniform1d = (PFNGLUNIFORM1DPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1d");
+ glUniform2d = (PFNGLUNIFORM2DPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2d");
+ glUniform3d = (PFNGLUNIFORM3DPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3d");
+ glUniform4d = (PFNGLUNIFORM4DPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4d");
+ glUniform1dv = (PFNGLUNIFORM1DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform1dv");
+ glUniform2dv = (PFNGLUNIFORM2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform2dv");
+ glUniform3dv = (PFNGLUNIFORM3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform3dv");
+ glUniform4dv = (PFNGLUNIFORM4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniform4dv");
+ glUniformMatrix2dv = (PFNGLUNIFORMMATRIX2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2dv");
+ glUniformMatrix3dv = (PFNGLUNIFORMMATRIX3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3dv");
+ glUniformMatrix4dv = (PFNGLUNIFORMMATRIX4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4dv");
+ glUniformMatrix2x3dv = (PFNGLUNIFORMMATRIX2X3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2x3dv");
+ glUniformMatrix2x4dv = (PFNGLUNIFORMMATRIX2X4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2x4dv");
+ glUniformMatrix3x2dv = (PFNGLUNIFORMMATRIX3X2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3x2dv");
+ glUniformMatrix3x4dv = (PFNGLUNIFORMMATRIX3X4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3x4dv");
+ glUniformMatrix4x2dv = (PFNGLUNIFORMMATRIX4X2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4x2dv");
+ glUniformMatrix4x3dv = (PFNGLUNIFORMMATRIX4X3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4x3dv");
+ glGetUniformdv = (PFNGLGETUNIFORMDVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformdv");
+ glGetSubroutineUniformLocation = (PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSubroutineUniformLocation");
+ glGetSubroutineIndex = (PFNGLGETSUBROUTINEINDEXPROC)GLH_EXT_GET_PROC_ADDRESS("glGetSubroutineIndex");
+ glGetActiveSubroutineUniformiv = (PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveSubroutineUniformiv");
+ glGetActiveSubroutineUniformName = (PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveSubroutineUniformName");
+ glGetActiveSubroutineName = (PFNGLGETACTIVESUBROUTINENAMEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveSubroutineName");
+ glUniformSubroutinesuiv = (PFNGLUNIFORMSUBROUTINESUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformSubroutinesuiv");
+ glGetUniformSubroutineuiv = (PFNGLGETUNIFORMSUBROUTINEUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetUniformSubroutineuiv");
+ glGetProgramStageiv = (PFNGLGETPROGRAMSTAGEIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramStageiv");
+ glPatchParameteri = (PFNGLPATCHPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glPatchParameteri");
+ glPatchParameterfv = (PFNGLPATCHPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glPatchParameterfv");
+ glBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glBindTransformFeedback");
+ glDeleteTransformFeedbacks = (PFNGLDELETETRANSFORMFEEDBACKSPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteTransformFeedbacks");
+ glGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC)GLH_EXT_GET_PROC_ADDRESS("glGenTransformFeedbacks");
+ glIsTransformFeedback = (PFNGLISTRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glIsTransformFeedback");
+ glPauseTransformFeedback = (PFNGLPAUSETRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glPauseTransformFeedback");
+ glResumeTransformFeedback = (PFNGLRESUMETRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glResumeTransformFeedback");
+ glDrawTransformFeedback = (PFNGLDRAWTRANSFORMFEEDBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawTransformFeedback");
+ glDrawTransformFeedbackStream = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawTransformFeedbackStream");
+ glBeginQueryIndexed = (PFNGLBEGINQUERYINDEXEDPROC)GLH_EXT_GET_PROC_ADDRESS("glBeginQueryIndexed");
+ glEndQueryIndexed = (PFNGLENDQUERYINDEXEDPROC)GLH_EXT_GET_PROC_ADDRESS("glEndQueryIndexed");
+ glGetQueryIndexediv = (PFNGLGETQUERYINDEXEDIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryIndexediv");
+
+ // GL_VERSION_4_1
+ if (mGLVersion < 4.09f)
+ {
+ return;
+ }
+ glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC)GLH_EXT_GET_PROC_ADDRESS("glReleaseShaderCompiler");
+ glShaderBinary = (PFNGLSHADERBINARYPROC)GLH_EXT_GET_PROC_ADDRESS("glShaderBinary");
+ glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glGetShaderPrecisionFormat");
+ glDepthRangef = (PFNGLDEPTHRANGEFPROC)GLH_EXT_GET_PROC_ADDRESS("glDepthRangef");
+ glClearDepthf = (PFNGLCLEARDEPTHFPROC)GLH_EXT_GET_PROC_ADDRESS("glClearDepthf");
+ glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramBinary");
+ glProgramBinary = (PFNGLPROGRAMBINARYPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramBinary");
+ glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramParameteri");
+ glUseProgramStages = (PFNGLUSEPROGRAMSTAGESPROC)GLH_EXT_GET_PROC_ADDRESS("glUseProgramStages");
+ glActiveShaderProgram = (PFNGLACTIVESHADERPROGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glActiveShaderProgram");
+ glCreateShaderProgramv = (PFNGLCREATESHADERPROGRAMVPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateShaderProgramv");
+ glBindProgramPipeline = (PFNGLBINDPROGRAMPIPELINEPROC)GLH_EXT_GET_PROC_ADDRESS("glBindProgramPipeline");
+ glDeleteProgramPipelines = (PFNGLDELETEPROGRAMPIPELINESPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteProgramPipelines");
+ glGenProgramPipelines = (PFNGLGENPROGRAMPIPELINESPROC)GLH_EXT_GET_PROC_ADDRESS("glGenProgramPipelines");
+ glIsProgramPipeline = (PFNGLISPROGRAMPIPELINEPROC)GLH_EXT_GET_PROC_ADDRESS("glIsProgramPipeline");
+ glGetProgramPipelineiv = (PFNGLGETPROGRAMPIPELINEIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramPipelineiv");
+ glProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1i");
+ glProgramUniform1iv = (PFNGLPROGRAMUNIFORM1IVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1iv");
+ glProgramUniform1f = (PFNGLPROGRAMUNIFORM1FPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1f");
+ glProgramUniform1fv = (PFNGLPROGRAMUNIFORM1FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1fv");
+ glProgramUniform1d = (PFNGLPROGRAMUNIFORM1DPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1d");
+ glProgramUniform1dv = (PFNGLPROGRAMUNIFORM1DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1dv");
+ glProgramUniform1ui = (PFNGLPROGRAMUNIFORM1UIPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1ui");
+ glProgramUniform1uiv = (PFNGLPROGRAMUNIFORM1UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform1uiv");
+ glProgramUniform2i = (PFNGLPROGRAMUNIFORM2IPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2i");
+ glProgramUniform2iv = (PFNGLPROGRAMUNIFORM2IVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2iv");
+ glProgramUniform2f = (PFNGLPROGRAMUNIFORM2FPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2f");
+ glProgramUniform2fv = (PFNGLPROGRAMUNIFORM2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2fv");
+ glProgramUniform2d = (PFNGLPROGRAMUNIFORM2DPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2d");
+ glProgramUniform2dv = (PFNGLPROGRAMUNIFORM2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2dv");
+ glProgramUniform2ui = (PFNGLPROGRAMUNIFORM2UIPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2ui");
+ glProgramUniform2uiv = (PFNGLPROGRAMUNIFORM2UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform2uiv");
+ glProgramUniform3i = (PFNGLPROGRAMUNIFORM3IPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3i");
+ glProgramUniform3iv = (PFNGLPROGRAMUNIFORM3IVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3iv");
+ glProgramUniform3f = (PFNGLPROGRAMUNIFORM3FPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3f");
+ glProgramUniform3fv = (PFNGLPROGRAMUNIFORM3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3fv");
+ glProgramUniform3d = (PFNGLPROGRAMUNIFORM3DPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3d");
+ glProgramUniform3dv = (PFNGLPROGRAMUNIFORM3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3dv");
+ glProgramUniform3ui = (PFNGLPROGRAMUNIFORM3UIPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3ui");
+ glProgramUniform3uiv = (PFNGLPROGRAMUNIFORM3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform3uiv");
+ glProgramUniform4i = (PFNGLPROGRAMUNIFORM4IPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4i");
+ glProgramUniform4iv = (PFNGLPROGRAMUNIFORM4IVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4iv");
+ glProgramUniform4f = (PFNGLPROGRAMUNIFORM4FPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4f");
+ glProgramUniform4fv = (PFNGLPROGRAMUNIFORM4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4fv");
+ glProgramUniform4d = (PFNGLPROGRAMUNIFORM4DPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4d");
+ glProgramUniform4dv = (PFNGLPROGRAMUNIFORM4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4dv");
+ glProgramUniform4ui = (PFNGLPROGRAMUNIFORM4UIPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4ui");
+ glProgramUniform4uiv = (PFNGLPROGRAMUNIFORM4UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniform4uiv");
+ glProgramUniformMatrix2fv = (PFNGLPROGRAMUNIFORMMATRIX2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix2fv");
+ glProgramUniformMatrix3fv = (PFNGLPROGRAMUNIFORMMATRIX3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix3fv");
+ glProgramUniformMatrix4fv = (PFNGLPROGRAMUNIFORMMATRIX4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix4fv");
+ glProgramUniformMatrix2dv = (PFNGLPROGRAMUNIFORMMATRIX2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix2dv");
+ glProgramUniformMatrix3dv = (PFNGLPROGRAMUNIFORMMATRIX3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix3dv");
+ glProgramUniformMatrix4dv = (PFNGLPROGRAMUNIFORMMATRIX4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix4dv");
+ glProgramUniformMatrix2x3fv = (PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix2x3fv");
+ glProgramUniformMatrix3x2fv = (PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix3x2fv");
+ glProgramUniformMatrix2x4fv = (PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix2x4fv");
+ glProgramUniformMatrix4x2fv = (PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix4x2fv");
+ glProgramUniformMatrix3x4fv = (PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix3x4fv");
+ glProgramUniformMatrix4x3fv = (PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix4x3fv");
+ glProgramUniformMatrix2x3dv = (PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix2x3dv");
+ glProgramUniformMatrix3x2dv = (PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix3x2dv");
+ glProgramUniformMatrix2x4dv = (PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix2x4dv");
+ glProgramUniformMatrix4x2dv = (PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix4x2dv");
+ glProgramUniformMatrix3x4dv = (PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix3x4dv");
+ glProgramUniformMatrix4x3dv = (PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glProgramUniformMatrix4x3dv");
+ glValidateProgramPipeline = (PFNGLVALIDATEPROGRAMPIPELINEPROC)GLH_EXT_GET_PROC_ADDRESS("glValidateProgramPipeline");
+ glGetProgramPipelineInfoLog = (PFNGLGETPROGRAMPIPELINEINFOLOGPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramPipelineInfoLog");
+ glVertexAttribL1d = (PFNGLVERTEXATTRIBL1DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL1d");
+ glVertexAttribL2d = (PFNGLVERTEXATTRIBL2DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL2d");
+ glVertexAttribL3d = (PFNGLVERTEXATTRIBL3DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL3d");
+ glVertexAttribL4d = (PFNGLVERTEXATTRIBL4DPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL4d");
+ glVertexAttribL1dv = (PFNGLVERTEXATTRIBL1DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL1dv");
+ glVertexAttribL2dv = (PFNGLVERTEXATTRIBL2DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL2dv");
+ glVertexAttribL3dv = (PFNGLVERTEXATTRIBL3DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL3dv");
+ glVertexAttribL4dv = (PFNGLVERTEXATTRIBL4DVPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribL4dv");
+ glVertexAttribLPointer = (PFNGLVERTEXATTRIBLPOINTERPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribLPointer");
+ glGetVertexAttribLdv = (PFNGLGETVERTEXATTRIBLDVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribLdv");
+ glViewportArrayv = (PFNGLVIEWPORTARRAYVPROC)GLH_EXT_GET_PROC_ADDRESS("glViewportArrayv");
+ glViewportIndexedf = (PFNGLVIEWPORTINDEXEDFPROC)GLH_EXT_GET_PROC_ADDRESS("glViewportIndexedf");
+ glViewportIndexedfv = (PFNGLVIEWPORTINDEXEDFVPROC)GLH_EXT_GET_PROC_ADDRESS("glViewportIndexedfv");
+ glScissorArrayv = (PFNGLSCISSORARRAYVPROC)GLH_EXT_GET_PROC_ADDRESS("glScissorArrayv");
+ glScissorIndexed = (PFNGLSCISSORINDEXEDPROC)GLH_EXT_GET_PROC_ADDRESS("glScissorIndexed");
+ glScissorIndexedv = (PFNGLSCISSORINDEXEDVPROC)GLH_EXT_GET_PROC_ADDRESS("glScissorIndexedv");
+ glDepthRangeArrayv = (PFNGLDEPTHRANGEARRAYVPROC)GLH_EXT_GET_PROC_ADDRESS("glDepthRangeArrayv");
+ glDepthRangeIndexed = (PFNGLDEPTHRANGEINDEXEDPROC)GLH_EXT_GET_PROC_ADDRESS("glDepthRangeIndexed");
+ glGetFloati_v = (PFNGLGETFLOATI_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetFloati_v");
+ glGetDoublei_v = (PFNGLGETDOUBLEI_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetDoublei_v");
+
+ // GL_VERSION_4_2
+ if (mGLVersion < 4.19f)
+ {
+ return;
+ }
+ glDrawArraysInstancedBaseInstance = (PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawArraysInstancedBaseInstance");
+ glDrawElementsInstancedBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsInstancedBaseInstance");
+ glDrawElementsInstancedBaseVertexBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsInstancedBaseVertexBaseInstance");
+ glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetInternalformativ");
+ glGetActiveAtomicCounterBufferiv = (PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetActiveAtomicCounterBufferiv");
+ glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC)GLH_EXT_GET_PROC_ADDRESS("glBindImageTexture");
+ glMemoryBarrier = (PFNGLMEMORYBARRIERPROC)GLH_EXT_GET_PROC_ADDRESS("glMemoryBarrier");
+ glTexStorage1D = (PFNGLTEXSTORAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glTexStorage1D");
+ glTexStorage2D = (PFNGLTEXSTORAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glTexStorage2D");
+ glTexStorage3D = (PFNGLTEXSTORAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glTexStorage3D");
+ glDrawTransformFeedbackInstanced = (PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawTransformFeedbackInstanced");
+ glDrawTransformFeedbackStreamInstanced = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawTransformFeedbackStreamInstanced");
+
+ // GL_VERSION_4_3
+ if (mGLVersion < 4.29f)
+ {
+ return;
+ }
+ glClearBufferData = (PFNGLCLEARBUFFERDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferData");
+ glClearBufferSubData = (PFNGLCLEARBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferSubData");
+ glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC)GLH_EXT_GET_PROC_ADDRESS("glDispatchCompute");
+ glDispatchComputeIndirect = (PFNGLDISPATCHCOMPUTEINDIRECTPROC)GLH_EXT_GET_PROC_ADDRESS("glDispatchComputeIndirect");
+ glCopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyImageSubData");
+ glFramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glFramebufferParameteri");
+ glGetFramebufferParameteriv = (PFNGLGETFRAMEBUFFERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetFramebufferParameteriv");
+ glGetInternalformati64v = (PFNGLGETINTERNALFORMATI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetInternalformati64v");
+ glInvalidateTexSubImage = (PFNGLINVALIDATETEXSUBIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateTexSubImage");
+ glInvalidateTexImage = (PFNGLINVALIDATETEXIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateTexImage");
+ glInvalidateBufferSubData = (PFNGLINVALIDATEBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateBufferSubData");
+ glInvalidateBufferData = (PFNGLINVALIDATEBUFFERDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateBufferData");
+ glInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateFramebuffer");
+ glInvalidateSubFramebuffer = (PFNGLINVALIDATESUBFRAMEBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateSubFramebuffer");
+ glMultiDrawArraysIndirect = (PFNGLMULTIDRAWARRAYSINDIRECTPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawArraysIndirect");
+ glMultiDrawElementsIndirect = (PFNGLMULTIDRAWELEMENTSINDIRECTPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawElementsIndirect");
+ glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramInterfaceiv");
+ glGetProgramResourceIndex = (PFNGLGETPROGRAMRESOURCEINDEXPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramResourceIndex");
+ glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramResourceName");
+ glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramResourceiv");
+ glGetProgramResourceLocation = (PFNGLGETPROGRAMRESOURCELOCATIONPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramResourceLocation");
+ glGetProgramResourceLocationIndex = (PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC)GLH_EXT_GET_PROC_ADDRESS("glGetProgramResourceLocationIndex");
+ glShaderStorageBlockBinding = (PFNGLSHADERSTORAGEBLOCKBINDINGPROC)GLH_EXT_GET_PROC_ADDRESS("glShaderStorageBlockBinding");
+ glTexBufferRange = (PFNGLTEXBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glTexBufferRange");
+ glTexStorage2DMultisample = (PFNGLTEXSTORAGE2DMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glTexStorage2DMultisample");
+ glTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glTexStorage3DMultisample");
+ glTextureView = (PFNGLTEXTUREVIEWPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureView");
+ glBindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glBindVertexBuffer");
+ glVertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribFormat");
+ glVertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribIFormat");
+ glVertexAttribLFormat = (PFNGLVERTEXATTRIBLFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribLFormat");
+ glVertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribBinding");
+ glVertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexBindingDivisor");
+ glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)GLH_EXT_GET_PROC_ADDRESS("glDebugMessageControl");
+ glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC)GLH_EXT_GET_PROC_ADDRESS("glDebugMessageInsert");
+ glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)GLH_EXT_GET_PROC_ADDRESS("glDebugMessageCallback");
+ glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC)GLH_EXT_GET_PROC_ADDRESS("glGetDebugMessageLog");
+ glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC)GLH_EXT_GET_PROC_ADDRESS("glPushDebugGroup");
+ glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC)GLH_EXT_GET_PROC_ADDRESS("glPopDebugGroup");
+ glObjectLabel = (PFNGLOBJECTLABELPROC)GLH_EXT_GET_PROC_ADDRESS("glObjectLabel");
+ glGetObjectLabel = (PFNGLGETOBJECTLABELPROC)GLH_EXT_GET_PROC_ADDRESS("glGetObjectLabel");
+ glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC)GLH_EXT_GET_PROC_ADDRESS("glObjectPtrLabel");
+ glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)GLH_EXT_GET_PROC_ADDRESS("glGetObjectPtrLabel");
+
+ // GL_VERSION_4_4
+ if (mGLVersion < 4.39f)
+ {
+ return;
+ }
+ glBufferStorage = (PFNGLBUFFERSTORAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glBufferStorage");
+ glClearTexImage = (PFNGLCLEARTEXIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glClearTexImage");
+ glClearTexSubImage = (PFNGLCLEARTEXSUBIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glClearTexSubImage");
+ glBindBuffersBase = (PFNGLBINDBUFFERSBASEPROC)GLH_EXT_GET_PROC_ADDRESS("glBindBuffersBase");
+ glBindBuffersRange = (PFNGLBINDBUFFERSRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glBindBuffersRange");
+ glBindTextures = (PFNGLBINDTEXTURESPROC)GLH_EXT_GET_PROC_ADDRESS("glBindTextures");
+ glBindSamplers = (PFNGLBINDSAMPLERSPROC)GLH_EXT_GET_PROC_ADDRESS("glBindSamplers");
+ glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC)GLH_EXT_GET_PROC_ADDRESS("glBindImageTextures");
+ glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glBindVertexBuffers");
+
+ // GL_VERSION_4_5
+ if (mGLVersion < 4.49f)
+ {
+ return;
+ }
+ glClipControl = (PFNGLCLIPCONTROLPROC)GLH_EXT_GET_PROC_ADDRESS("glClipControl");
+ glCreateTransformFeedbacks = (PFNGLCREATETRANSFORMFEEDBACKSPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateTransformFeedbacks");
+ glTransformFeedbackBufferBase = (PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC)GLH_EXT_GET_PROC_ADDRESS("glTransformFeedbackBufferBase");
+ glTransformFeedbackBufferRange = (PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glTransformFeedbackBufferRange");
+ glGetTransformFeedbackiv = (PFNGLGETTRANSFORMFEEDBACKIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTransformFeedbackiv");
+ glGetTransformFeedbacki_v = (PFNGLGETTRANSFORMFEEDBACKI_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTransformFeedbacki_v");
+ glGetTransformFeedbacki64_v = (PFNGLGETTRANSFORMFEEDBACKI64_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTransformFeedbacki64_v");
+ glCreateBuffers = (PFNGLCREATEBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateBuffers");
+ glNamedBufferStorage = (PFNGLNAMEDBUFFERSTORAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedBufferStorage");
+ glNamedBufferData = (PFNGLNAMEDBUFFERDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedBufferData");
+ glNamedBufferSubData = (PFNGLNAMEDBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedBufferSubData");
+ glCopyNamedBufferSubData = (PFNGLCOPYNAMEDBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyNamedBufferSubData");
+ glClearNamedBufferData = (PFNGLCLEARNAMEDBUFFERDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glClearNamedBufferData");
+ glClearNamedBufferSubData = (PFNGLCLEARNAMEDBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glClearNamedBufferSubData");
+ glMapNamedBuffer = (PFNGLMAPNAMEDBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glMapNamedBuffer");
+ glMapNamedBufferRange = (PFNGLMAPNAMEDBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glMapNamedBufferRange");
+ glUnmapNamedBuffer = (PFNGLUNMAPNAMEDBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glUnmapNamedBuffer");
+ glFlushMappedNamedBufferRange = (PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glFlushMappedNamedBufferRange");
+ glGetNamedBufferParameteriv = (PFNGLGETNAMEDBUFFERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedBufferParameteriv");
+ glGetNamedBufferParameteri64v = (PFNGLGETNAMEDBUFFERPARAMETERI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedBufferParameteri64v");
+ glGetNamedBufferPointerv = (PFNGLGETNAMEDBUFFERPOINTERVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedBufferPointerv");
+ glGetNamedBufferSubData = (PFNGLGETNAMEDBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedBufferSubData");
+ glCreateFramebuffers = (PFNGLCREATEFRAMEBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateFramebuffers");
+ glNamedFramebufferRenderbuffer = (PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferRenderbuffer");
+ glNamedFramebufferParameteri = (PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferParameteri");
+ glNamedFramebufferTexture = (PFNGLNAMEDFRAMEBUFFERTEXTUREPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferTexture");
+ glNamedFramebufferTextureLayer = (PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferTextureLayer");
+ glNamedFramebufferDrawBuffer = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferDrawBuffer");
+ glNamedFramebufferDrawBuffers = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferDrawBuffers");
+ glNamedFramebufferReadBuffer = (PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedFramebufferReadBuffer");
+ glInvalidateNamedFramebufferData = (PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateNamedFramebufferData");
+ glInvalidateNamedFramebufferSubData = (PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glInvalidateNamedFramebufferSubData");
+ glClearNamedFramebufferiv = (PFNGLCLEARNAMEDFRAMEBUFFERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glClearNamedFramebufferiv");
+ glClearNamedFramebufferuiv = (PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glClearNamedFramebufferuiv");
+ glClearNamedFramebufferfv = (PFNGLCLEARNAMEDFRAMEBUFFERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glClearNamedFramebufferfv");
+ glClearNamedFramebufferfi = (PFNGLCLEARNAMEDFRAMEBUFFERFIPROC)GLH_EXT_GET_PROC_ADDRESS("glClearNamedFramebufferfi");
+ glBlitNamedFramebuffer = (PFNGLBLITNAMEDFRAMEBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glBlitNamedFramebuffer");
+ glCheckNamedFramebufferStatus = (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)GLH_EXT_GET_PROC_ADDRESS("glCheckNamedFramebufferStatus");
+ glGetNamedFramebufferParameteriv = (PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedFramebufferParameteriv");
+ glGetNamedFramebufferAttachmentParameteriv = (PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedFramebufferAttachmentParameteriv");
+ glCreateRenderbuffers = (PFNGLCREATERENDERBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateRenderbuffers");
+ glNamedRenderbufferStorage = (PFNGLNAMEDRENDERBUFFERSTORAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedRenderbufferStorage");
+ glNamedRenderbufferStorageMultisample = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glNamedRenderbufferStorageMultisample");
+ glGetNamedRenderbufferParameteriv = (PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetNamedRenderbufferParameteriv");
+ glCreateTextures = (PFNGLCREATETEXTURESPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateTextures");
+ glTextureBuffer = (PFNGLTEXTUREBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureBuffer");
+ glTextureBufferRange = (PFNGLTEXTUREBUFFERRANGEPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureBufferRange");
+ glTextureStorage1D = (PFNGLTEXTURESTORAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureStorage1D");
+ glTextureStorage2D = (PFNGLTEXTURESTORAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureStorage2D");
+ glTextureStorage3D = (PFNGLTEXTURESTORAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureStorage3D");
+ glTextureStorage2DMultisample = (PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureStorage2DMultisample");
+ glTextureStorage3DMultisample = (PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureStorage3DMultisample");
+ glTextureSubImage1D = (PFNGLTEXTURESUBIMAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureSubImage1D");
+ glTextureSubImage2D = (PFNGLTEXTURESUBIMAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureSubImage2D");
+ glTextureSubImage3D = (PFNGLTEXTURESUBIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureSubImage3D");
+ glCompressedTextureSubImage1D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTextureSubImage1D");
+ glCompressedTextureSubImage2D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTextureSubImage2D");
+ glCompressedTextureSubImage3D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTextureSubImage3D");
+ glCopyTextureSubImage1D = (PFNGLCOPYTEXTURESUBIMAGE1DPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyTextureSubImage1D");
+ glCopyTextureSubImage2D = (PFNGLCOPYTEXTURESUBIMAGE2DPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyTextureSubImage2D");
+ glCopyTextureSubImage3D = (PFNGLCOPYTEXTURESUBIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glCopyTextureSubImage3D");
+ glTextureParameterf = (PFNGLTEXTUREPARAMETERFPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureParameterf");
+ glTextureParameterfv = (PFNGLTEXTUREPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureParameterfv");
+ glTextureParameteri = (PFNGLTEXTUREPARAMETERIPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureParameteri");
+ glTextureParameterIiv = (PFNGLTEXTUREPARAMETERIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureParameterIiv");
+ glTextureParameterIuiv = (PFNGLTEXTUREPARAMETERIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureParameterIuiv");
+ glTextureParameteriv = (PFNGLTEXTUREPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureParameteriv");
+ glGenerateTextureMipmap = (PFNGLGENERATETEXTUREMIPMAPPROC)GLH_EXT_GET_PROC_ADDRESS("glGenerateTextureMipmap");
+ glBindTextureUnit = (PFNGLBINDTEXTUREUNITPROC)GLH_EXT_GET_PROC_ADDRESS("glBindTextureUnit");
+ glGetTextureImage = (PFNGLGETTEXTUREIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureImage");
+ glGetCompressedTextureImage = (PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetCompressedTextureImage");
+ glGetTextureLevelParameterfv = (PFNGLGETTEXTURELEVELPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureLevelParameterfv");
+ glGetTextureLevelParameteriv = (PFNGLGETTEXTURELEVELPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureLevelParameteriv");
+ glGetTextureParameterfv = (PFNGLGETTEXTUREPARAMETERFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureParameterfv");
+ glGetTextureParameterIiv = (PFNGLGETTEXTUREPARAMETERIIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureParameterIiv");
+ glGetTextureParameterIuiv = (PFNGLGETTEXTUREPARAMETERIUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureParameterIuiv");
+ glGetTextureParameteriv = (PFNGLGETTEXTUREPARAMETERIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureParameteriv");
+ glCreateVertexArrays = (PFNGLCREATEVERTEXARRAYSPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateVertexArrays");
+ glDisableVertexArrayAttrib = (PFNGLDISABLEVERTEXARRAYATTRIBPROC)GLH_EXT_GET_PROC_ADDRESS("glDisableVertexArrayAttrib");
+ glEnableVertexArrayAttrib = (PFNGLENABLEVERTEXARRAYATTRIBPROC)GLH_EXT_GET_PROC_ADDRESS("glEnableVertexArrayAttrib");
+ glVertexArrayElementBuffer = (PFNGLVERTEXARRAYELEMENTBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayElementBuffer");
+ glVertexArrayVertexBuffer = (PFNGLVERTEXARRAYVERTEXBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayVertexBuffer");
+ glVertexArrayVertexBuffers = (PFNGLVERTEXARRAYVERTEXBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayVertexBuffers");
+ glVertexArrayAttribBinding = (PFNGLVERTEXARRAYATTRIBBINDINGPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayAttribBinding");
+ glVertexArrayAttribFormat = (PFNGLVERTEXARRAYATTRIBFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayAttribFormat");
+ glVertexArrayAttribIFormat = (PFNGLVERTEXARRAYATTRIBIFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayAttribIFormat");
+ glVertexArrayAttribLFormat = (PFNGLVERTEXARRAYATTRIBLFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayAttribLFormat");
+ glVertexArrayBindingDivisor = (PFNGLVERTEXARRAYBINDINGDIVISORPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexArrayBindingDivisor");
+ glGetVertexArrayiv = (PFNGLGETVERTEXARRAYIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexArrayiv");
+ glGetVertexArrayIndexediv = (PFNGLGETVERTEXARRAYINDEXEDIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexArrayIndexediv");
+ glGetVertexArrayIndexed64iv = (PFNGLGETVERTEXARRAYINDEXED64IVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVertexArrayIndexed64iv");
+ glCreateSamplers = (PFNGLCREATESAMPLERSPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateSamplers");
+ glCreateProgramPipelines = (PFNGLCREATEPROGRAMPIPELINESPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateProgramPipelines");
+ glCreateQueries = (PFNGLCREATEQUERIESPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateQueries");
+ glGetQueryBufferObjecti64v = (PFNGLGETQUERYBUFFEROBJECTI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryBufferObjecti64v");
+ glGetQueryBufferObjectiv = (PFNGLGETQUERYBUFFEROBJECTIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryBufferObjectiv");
+ glGetQueryBufferObjectui64v = (PFNGLGETQUERYBUFFEROBJECTUI64VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryBufferObjectui64v");
+ glGetQueryBufferObjectuiv = (PFNGLGETQUERYBUFFEROBJECTUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryBufferObjectuiv");
+ glMemoryBarrierByRegion = (PFNGLMEMORYBARRIERBYREGIONPROC)GLH_EXT_GET_PROC_ADDRESS("glMemoryBarrierByRegion");
+ glGetTextureSubImage = (PFNGLGETTEXTURESUBIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetTextureSubImage");
+ glGetCompressedTextureSubImage = (PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetCompressedTextureSubImage");
+ glGetGraphicsResetStatus = (PFNGLGETGRAPHICSRESETSTATUSPROC)GLH_EXT_GET_PROC_ADDRESS("glGetGraphicsResetStatus");
+ glGetnCompressedTexImage = (PFNGLGETNCOMPRESSEDTEXIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnCompressedTexImage");
+ glGetnTexImage = (PFNGLGETNTEXIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnTexImage");
+ glGetnUniformdv = (PFNGLGETNUNIFORMDVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnUniformdv");
+ glGetnUniformfv = (PFNGLGETNUNIFORMFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnUniformfv");
+ glGetnUniformiv = (PFNGLGETNUNIFORMIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnUniformiv");
+ glGetnUniformuiv = (PFNGLGETNUNIFORMUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnUniformuiv");
+ glReadnPixels = (PFNGLREADNPIXELSPROC)GLH_EXT_GET_PROC_ADDRESS("glReadnPixels");
+ glGetnMapdv = (PFNGLGETNMAPDVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnMapdv");
+ glGetnMapfv = (PFNGLGETNMAPFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnMapfv");
+ glGetnMapiv = (PFNGLGETNMAPIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnMapiv");
+ glGetnPixelMapfv = (PFNGLGETNPIXELMAPFVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnPixelMapfv");
+ glGetnPixelMapuiv = (PFNGLGETNPIXELMAPUIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnPixelMapuiv");
+ glGetnPixelMapusv = (PFNGLGETNPIXELMAPUSVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnPixelMapusv");
+ glGetnPolygonStipple = (PFNGLGETNPOLYGONSTIPPLEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnPolygonStipple");
+ glGetnColorTable = (PFNGLGETNCOLORTABLEPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnColorTable");
+ glGetnConvolutionFilter = (PFNGLGETNCONVOLUTIONFILTERPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnConvolutionFilter");
+ glGetnSeparableFilter = (PFNGLGETNSEPARABLEFILTERPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnSeparableFilter");
+ glGetnHistogram = (PFNGLGETNHISTOGRAMPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnHistogram");
+ glGetnMinmax = (PFNGLGETNMINMAXPROC)GLH_EXT_GET_PROC_ADDRESS("glGetnMinmax");
+ glTextureBarrier = (PFNGLTEXTUREBARRIERPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureBarrier");
+
+ // GL_VERSION_4_6
+ if (mGLVersion < 4.59f)
+ {
+ return;
+ }
+ glSpecializeShader = (PFNGLSPECIALIZESHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glSpecializeShader");
+ glMultiDrawArraysIndirectCount = (PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawArraysIndirectCount");
+ glMultiDrawElementsIndirectCount = (PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawElementsIndirectCount");
+ glPolygonOffsetClamp = (PFNGLPOLYGONOFFSETCLAMPPROC)GLH_EXT_GET_PROC_ADDRESS("glPolygonOffsetClamp");
+
+#endif
+}
+
+void rotate_quat(LLQuaternion& rotation)
+{
+ F32 angle_radians, x, y, z;
+ rotation.getAngleAxis(&angle_radians, &x, &y, &z);
+ gGL.rotatef(angle_radians * RAD_TO_DEG, x, y, z);
+}
+
+void flush_glerror()
+{
+ glGetError();
+}
+
+//this function outputs gl error to the log file, does not crash the code.
+void log_glerror()
+{
+ if (LL_UNLIKELY(!gGLManager.mInited))
+ {
+ return ;
+ }
+ // Create or update texture to be used with this data
+ GLenum error;
+ error = glGetError();
+ while (LL_UNLIKELY(error))
+ {
+ GLubyte const * gl_error_msg = gluErrorString(error);
+ if (NULL != gl_error_msg)
+ {
+ LL_WARNS() << "GL Error: " << error << " GL Error String: " << gl_error_msg << LL_ENDL ;
+ }
+ else
+ {
+ // gluErrorString returns NULL for some extensions' error codes.
+ // you'll probably have to grep for the number in glext.h.
+ LL_WARNS() << "GL Error: UNKNOWN 0x" << std::hex << error << std::dec << LL_ENDL;
+ }
+ error = glGetError();
+ }
+}
+
+void do_assert_glerror()
+{
+ // Create or update texture to be used with this data
+ GLenum error;
+ error = glGetError();
+ bool quit = false;
+ if (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;
+ }
+ }
+ }
+
+ if (quit)
+ {
+ if (gDebugSession)
+ {
+ ll_fail("assert_glerror failed");
+ }
+ else
+ {
+ LL_ERRS() << "One or more unhandled GL errors." << LL_ENDL;
+ }
+ }
+}
+
+void assert_glerror()
+{
+/* if (!gGLActive)
+ {
+ //LL_WARNS() << "GL used while not active!" << LL_ENDL;
+
+ if (gDebugSession)
+ {
+ //ll_fail("GL used while not active");
+ }
+ }
+*/
+
+ if (!gDebugGL)
+ {
+ //funny looking if for branch prediction -- gDebugGL is almost always false and assert_glerror is called often
+ }
+ else
+ {
+ do_assert_glerror();
+ }
+}
+
+
+void clear_glerror()
+{
+ glGetError();
+ 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] = GL_FALSE;
+ glDisable(GL_MULTISAMPLE);
+}
+
+//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();
+ glClientActiveTexture(GL_TEXTURE0+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(GLboolean writeAlpha)
+{
+ if (!gDebugGL)
+ {
+ return;
+ }
+
+ GLint src;
+ GLint dst;
+ glGetIntegerv(GL_BLEND_SRC, &src);
+ glGetIntegerv(GL_BLEND_DST, &dst);
+ llassert_always(src == GL_SRC_ALPHA);
+ llassert_always(dst == GL_ONE_MINUS_SRC_ALPHA);
+
+ // disable for now until usage is consistent
+ //GLboolean colorMask[4];
+ //glGetBooleanv(GL_COLOR_WRITEMASK, colorMask);
+ //llassert_always(colorMask[0]);
+ //llassert_always(colorMask[1]);
+ //llassert_always(colorMask[2]);
+ // llassert_always(colorMask[3] == writeAlpha);
+
+ 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();
+ LL_GL_ERRS << llformat("LLGLState error. State: 0x%04x",state) << LL_ENDL;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////
+
+LLGLState::LLGLState(LLGLenum state, S32 enabled) :
+ mState(state), mWasEnabled(false), mIsEnabled(false)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE;
+
+ if (mState)
+ {
+ mWasEnabled = sStateMap[state];
+ setEnabled(enabled);
+ }
+}
+
+void LLGLState::setEnabled(S32 enabled)
+{
+ if (!mState)
+ {
+ return;
+ }
+ if (enabled == CURRENT_STATE)
+ {
+ enabled = sStateMap[mState] == GL_TRUE ? ENABLED_STATE : DISABLED_STATE;
+ }
+ else if (enabled == ENABLED_STATE && sStateMap[mState] != GL_TRUE)
+ {
+ gGL.flush();
+ glEnable(mState);
+ sStateMap[mState] = GL_TRUE;
+ }
+ else if (enabled == DISABLED_STATE && sStateMap[mState] != GL_FALSE)
+ {
+ gGL.flush();
+ glDisable(mState);
+ sStateMap[mState] = GL_FALSE;
+ }
+ mIsEnabled = enabled;
+}
+
+LLGLState::~LLGLState()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE;
+ 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;
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void LLGLManager::initGLStates()
+{
+ //gl states moved to classes in llglstates.h
+ LLGLState::initClass();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific, std::string* version_string )
+{
+ // 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;
+ }
+
+ version_string->assign(version);
+
+ 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 );
+ }
+}
+
+
+void parse_glsl_version(S32& major, S32& minor)
+{
+ // GL_SHADING_LANGUAGE_VERSION returns a null-terminated string with the format:
+ // <major>.<minor>[.<release>] [<vendor specific>]
+
+ const char* version = (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION);
+ major = 0;
+ minor = 0;
+
+ 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);
+}
+
+LLGLUserClipPlane::LLGLUserClipPlane(const LLPlane& p, const glh::matrix4f& modelview, const glh::matrix4f& projection, bool apply)
+{
+ mApply = apply;
+
+ if (mApply)
+ {
+ mModelview = modelview;
+ mProjection = projection;
+
+ //flip incoming LLPlane to get consistent behavior compared to frustum culling
+ setPlane(-p[0], -p[1], -p[2], -p[3]);
+ }
+}
+
+void LLGLUserClipPlane::disable()
+{
+ if (mApply)
+ {
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.popMatrix();
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ }
+ mApply = false;
+}
+
+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;
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.pushMatrix();
+ gGL.loadMatrix(newP.m);
+ gGLObliqueProjectionInverse = LLMatrix4(newP.inverse().transpose().m);
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+}
+
+LLGLUserClipPlane::~LLGLUserClipPlane()
+{
+ disable();
+}
+
+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 = GL_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 = GL_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;
+ }
+ }
+ }
+}
+
+LLGLSquashToFarClip::LLGLSquashToFarClip()
+{
+ glh::matrix4f proj = get_current_projection();
+ setProjectionMatrix(proj, 0);
+}
+
+LLGLSquashToFarClip::LLGLSquashToFarClip(glh::matrix4f& P, U32 layer)
+{
+ setProjectionMatrix(P, layer);
+}
+
+
+void LLGLSquashToFarClip::setProjectionMatrix(glh::matrix4f& projection, U32 layer)
+{
+
+ F32 depth = 0.99999f - 0.0001f * layer;
+
+ for (U32 i = 0; i < 4; i++)
+ {
+ projection.element(2, i) = projection.element(3, i) * depth;
+ }
+
+ LLRender::eMatrixMode last_matrix_mode = gGL.getMatrixMode();
+
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.pushMatrix();
+ gGL.loadMatrix(projection.m);
+
+ gGL.matrixMode(last_matrix_mode);
+}
+
+LLGLSquashToFarClip::~LLGLSquashToFarClip()
+{
+ LLRender::eMatrixMode last_matrix_mode = gGL.getMatrixMode();
+
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.popMatrix();
+
+ gGL.matrixMode(last_matrix_mode);
+}
+
+
+
+LLGLSyncFence::LLGLSyncFence()
+{
+ mSync = 0;
+}
+
+LLGLSyncFence::~LLGLSyncFence()
+{
+ if (mSync)
+ {
+ glDeleteSync(mSync);
+ }
+}
+
+void LLGLSyncFence::placeFence()
+{
+ if (mSync)
+ {
+ glDeleteSync(mSync);
+ }
+ mSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+}
+
+bool LLGLSyncFence::isCompleted()
+{
+ bool ret = true;
+ if (mSync)
+ {
+ GLenum status = glClientWaitSync(mSync, 0, 1);
+ if (status == GL_TIMEOUT_EXPIRED)
+ {
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+void LLGLSyncFence::wait()
+{
+ if (mSync)
+ {
+ while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED)
+ { //track the number of times we've waited here
+ }
+ }
+}
+
+LLGLSPipelineSkyBox::LLGLSPipelineSkyBox()
+: mCullFace(GL_CULL_FACE)
+, mSquashClip()
+{
+}
+
+LLGLSPipelineSkyBox::~LLGLSPipelineSkyBox()
+{
+}
+
+LLGLSPipelineDepthTestSkyBox::LLGLSPipelineDepthTestSkyBox(bool depth_test, bool depth_write)
+: LLGLSPipelineSkyBox()
+, mDepth(depth_test ? GL_TRUE : GL_FALSE, depth_write ? GL_TRUE : GL_FALSE, GL_LEQUAL)
+{
+
+}
+
+LLGLSPipelineBlendSkyBox::LLGLSPipelineBlendSkyBox(bool depth_test, bool depth_write)
+: LLGLSPipelineDepthTestSkyBox(depth_test, depth_write)
+, mBlend(GL_BLEND)
+{
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+}
+
+#if LL_WINDOWS
+// Expose desired use of high-performance graphics processor to Optimus driver and to AMD driver
+// https://docs.nvidia.com/gameworks/content/technologies/desktop/optimus.htm
+extern "C"
+{
+ __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
+ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
+}
+#endif
+
+
diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h
index f8303b1bff..75a7c5d3b2 100644
--- a/indra/llrender/llgl.h
+++ b/indra/llrender/llgl.h
@@ -1,476 +1,476 @@
-/**
- * @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 "llinstancetracker.h"
-
-#include "llglheaders.h"
-#include "glh/glh_linear.h"
-
-extern bool gDebugGL;
-extern bool gDebugSession;
-extern bool gDebugGLSession;
-extern llofstream 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;
-
- // OpenGL limits
- S32 mMaxSamples;
- S32 mNumTextureImageUnits;
- S32 mMaxSampleMaskWords;
- S32 mMaxColorTextureSamples;
- S32 mMaxDepthTextureSamples;
- S32 mMaxIntegerSamples;
- S32 mGLMaxVertexRange;
- S32 mGLMaxIndexRange;
- S32 mGLMaxTextureSize;
- F32 mMaxAnisotropy = 0.f;
-
- // GL 4.x capabilities
- bool mHasCubeMapArray = false;
- bool mHasDebugOutput = false;
- bool mHasTransformFeedback = false;
- bool mHasAnisotropic = false;
-
- // Vendor-specific extensions
- bool mHasAMDAssociations = false;
-
- bool mIsAMD;
- bool mIsNVIDIA;
- bool mIsIntel;
-
-#if LL_DARWIN
- // Needed to distinguish problem cards on older Macs that break with Materials
- bool mIsMobileGF;
-#endif
-
- // Whether this version of GL is good enough for SL to use
- bool mHasRequirements;
-
- S32 mDriverVersionMajor;
- S32 mDriverVersionMinor;
- S32 mDriverVersionRelease;
- F32 mGLVersion; // e.g = 1.4
- S32 mGLSLVersionMajor;
- S32 mGLSLVersionMinor;
- std::string mDriverVersionVendorString;
- std::string mGLVersionString;
-
- S32 mVRAM; // VRAM in MB
-
- void getPixelFormat(); // Get the best pixel format
-
- std::string getGLInfoString();
- void printGLInfoString();
- void getGLInfo(LLSD& info);
-
- void asLLSD(LLSD& info);
-
- // In ALL CAPS
- std::string mGLVendor;
- std::string mGLVendorShort;
-
- // In ALL CAPS
- std::string mGLRenderer;
-
-private:
- void initExtensions();
- void initGLStates();
- void initGLImages();
-};
-
-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 log_glerror();
-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 blend(GL_BLEND);
- renderHUD();
- LLGLDisable blend(GL_BLEND);
-
- //CORRECT USAGE
- {
- LLGLEnable blend(GL_BLEND);
- renderHUD();
- }
-
- If a state is to be set on a conditional, the following mechanism
- is useful:
-
- {
- LLGLEnable blend(blend_hud ? GL_GL_BLEND: 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.
-
-*/
-
-#include "boost/function.hpp"
-
-class LLGLState
-{
-public:
- static void initClass();
- static void restoreGL();
-
- static void resetTextureStates();
- static void dumpStates();
-
- // make sure GL blend function, GL states, and GL color mask match
- // what we expect
- // writeAlpha - whether or not writing to alpha channel is expected
- static void checkStates(GLboolean writeAlpha = GL_TRUE);
-
-protected:
- static boost::unordered_map<LLGLenum, LLGLboolean> sStateMap;
-
-public:
- enum { CURRENT_STATE = -2, DISABLED_STATE = 0, ENABLED_STATE = 1 };
- LLGLState(LLGLenum state, S32 enabled = CURRENT_STATE);
- ~LLGLState();
- void setEnabled(S32 enabled);
- void enable() { setEnabled(ENABLED_STATE); }
- void disable() { setEnabled(DISABLED_STATE); }
-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);
-};
-
-// Enable with functor
-class LLGLEnableFunc : LLGLState
-{
-public:
- LLGLEnableFunc(LLGLenum state, bool enable, boost::function<void()> func)
- : LLGLState(state, enable)
- {
- if (enable)
- {
- func();
- }
- }
-};
-
-/// TODO: Being deprecated.
-class LLGLEnable : public LLGLState
-{
-public:
- LLGLEnable(LLGLenum state) : LLGLState(state, ENABLED_STATE) {}
-};
-
-/// TODO: Being deprecated.
-class LLGLDisable : public LLGLState
-{
-public:
- LLGLDisable(LLGLenum state) : LLGLState(state, DISABLED_STATE) {}
-};
-
-/*
- 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, bool apply = true);
- ~LLGLUserClipPlane();
-
- void setPlane(F32 a, F32 b, F32 c, F32 d);
- void disable();
-
-private:
- bool mApply;
-
- glh::matrix4f mProjection;
- glh::matrix4f mModelview;
-};
-
-/*
- Modify and load projection matrix to push depth values to far clip plane.
-
- Restores projection matrix on destruction.
- Saves/restores matrix mode around projection manipulation.
- Does not stack.
-*/
-class LLGLSquashToFarClip
-{
-public:
- LLGLSquashToFarClip();
- LLGLSquashToFarClip(glh::matrix4f& projection, U32 layer = 0);
-
- void setProjectionMatrix(glh::matrix4f& projection, U32 layer);
-
- ~LLGLSquashToFarClip();
-};
-
-/*
- 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;
-};
-
-const U32 FENCE_WAIT_TIME_NANOSECONDS = 1000; //1 ms
-
-class LLGLFence
-{
-public:
- virtual ~LLGLFence()
- {
- }
-
- virtual void placeFence() = 0;
- virtual bool isCompleted() = 0;
- virtual void wait() = 0;
-};
-
-class LLGLSyncFence : public LLGLFence
-{
-public:
- GLsync mSync;
-
- LLGLSyncFence();
- virtual ~LLGLSyncFence();
-
- void placeFence();
- bool isCompleted();
- void wait();
-};
-
-extern LLMatrix4 gGLObliqueProjectionInverse;
-
-#include "llglstates.h"
-
-void init_glstates();
-
-void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific, std::string* version_string );
-
-extern bool gClothRipple;
-extern bool gHeadlessClient;
-extern bool gNonInteractive;
-extern bool gGLActive;
-
-// Deal with changing glext.h definitions for newer SDK versions, specifically
-// with MAC OSX 10.5 -> 10.6
-
-
-#ifndef GL_DEPTH_ATTACHMENT
-#define GL_DEPTH_ATTACHMENT GL_DEPTH_ATTACHMENT_EXT
-#endif
-
-#ifndef GL_STENCIL_ATTACHMENT
-#define GL_STENCIL_ATTACHMENT GL_STENCIL_ATTACHMENT_EXT
-#endif
-
-#ifndef GL_FRAMEBUFFER
-#define GL_FRAMEBUFFER GL_FRAMEBUFFER_EXT
-#define GL_DRAW_FRAMEBUFFER GL_DRAW_FRAMEBUFFER_EXT
-#define GL_READ_FRAMEBUFFER GL_READ_FRAMEBUFFER_EXT
-#define GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_EXT
-#define GL_FRAMEBUFFER_UNSUPPORTED GL_FRAMEBUFFER_UNSUPPORTED_EXT
-#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT
-#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT
-#define glGenFramebuffers glGenFramebuffersEXT
-#define glBindFramebuffer glBindFramebufferEXT
-#define glCheckFramebufferStatus glCheckFramebufferStatusEXT
-#define glBlitFramebuffer glBlitFramebufferEXT
-#define glDeleteFramebuffers glDeleteFramebuffersEXT
-#define glFramebufferRenderbuffer glFramebufferRenderbufferEXT
-#define glFramebufferTexture2D glFramebufferTexture2DEXT
-#endif
-
-#ifndef GL_RENDERBUFFER
-#define GL_RENDERBUFFER GL_RENDERBUFFER_EXT
-#define glGenRenderbuffers glGenRenderbuffersEXT
-#define glBindRenderbuffer glBindRenderbufferEXT
-#define glRenderbufferStorage glRenderbufferStorageEXT
-#define glRenderbufferStorageMultisample glRenderbufferStorageMultisampleEXT
-#define glDeleteRenderbuffers glDeleteRenderbuffersEXT
-#endif
-
-#ifndef GL_COLOR_ATTACHMENT
-#define GL_COLOR_ATTACHMENT GL_COLOR_ATTACHMENT_EXT
-#endif
-
-#ifndef GL_COLOR_ATTACHMENT0
-#define GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_EXT
-#endif
-
-#ifndef GL_COLOR_ATTACHMENT1
-#define GL_COLOR_ATTACHMENT1 GL_COLOR_ATTACHMENT1_EXT
-#endif
-
-#ifndef GL_COLOR_ATTACHMENT2
-#define GL_COLOR_ATTACHMENT2 GL_COLOR_ATTACHMENT2_EXT
-#endif
-
-#ifndef GL_COLOR_ATTACHMENT3
-#define GL_COLOR_ATTACHMENT3 GL_COLOR_ATTACHMENT3_EXT
-#endif
-
-
-#ifndef GL_DEPTH24_STENCIL8
-#define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_EXT
-#endif
-
-#endif // LL_LLGL_H
+/**
+ * @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 "llinstancetracker.h"
+
+#include "llglheaders.h"
+#include "glh/glh_linear.h"
+
+extern bool gDebugGL;
+extern bool gDebugSession;
+extern bool gDebugGLSession;
+extern llofstream 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;
+
+ // OpenGL limits
+ S32 mMaxSamples;
+ S32 mNumTextureImageUnits;
+ S32 mMaxSampleMaskWords;
+ S32 mMaxColorTextureSamples;
+ S32 mMaxDepthTextureSamples;
+ S32 mMaxIntegerSamples;
+ S32 mGLMaxVertexRange;
+ S32 mGLMaxIndexRange;
+ S32 mGLMaxTextureSize;
+ F32 mMaxAnisotropy = 0.f;
+
+ // GL 4.x capabilities
+ bool mHasCubeMapArray = false;
+ bool mHasDebugOutput = false;
+ bool mHasTransformFeedback = false;
+ bool mHasAnisotropic = false;
+
+ // Vendor-specific extensions
+ bool mHasAMDAssociations = false;
+
+ bool mIsAMD;
+ bool mIsNVIDIA;
+ bool mIsIntel;
+
+#if LL_DARWIN
+ // Needed to distinguish problem cards on older Macs that break with Materials
+ bool mIsMobileGF;
+#endif
+
+ // Whether this version of GL is good enough for SL to use
+ bool mHasRequirements;
+
+ S32 mDriverVersionMajor;
+ S32 mDriverVersionMinor;
+ S32 mDriverVersionRelease;
+ F32 mGLVersion; // e.g = 1.4
+ S32 mGLSLVersionMajor;
+ S32 mGLSLVersionMinor;
+ std::string mDriverVersionVendorString;
+ std::string mGLVersionString;
+
+ S32 mVRAM; // VRAM in MB
+
+ void getPixelFormat(); // Get the best pixel format
+
+ std::string getGLInfoString();
+ void printGLInfoString();
+ void getGLInfo(LLSD& info);
+
+ void asLLSD(LLSD& info);
+
+ // In ALL CAPS
+ std::string mGLVendor;
+ std::string mGLVendorShort;
+
+ // In ALL CAPS
+ std::string mGLRenderer;
+
+private:
+ void initExtensions();
+ void initGLStates();
+ void initGLImages();
+};
+
+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 log_glerror();
+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 blend(GL_BLEND);
+ renderHUD();
+ LLGLDisable blend(GL_BLEND);
+
+ //CORRECT USAGE
+ {
+ LLGLEnable blend(GL_BLEND);
+ renderHUD();
+ }
+
+ If a state is to be set on a conditional, the following mechanism
+ is useful:
+
+ {
+ LLGLEnable blend(blend_hud ? GL_GL_BLEND: 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.
+
+*/
+
+#include "boost/function.hpp"
+
+class LLGLState
+{
+public:
+ static void initClass();
+ static void restoreGL();
+
+ static void resetTextureStates();
+ static void dumpStates();
+
+ // make sure GL blend function, GL states, and GL color mask match
+ // what we expect
+ // writeAlpha - whether or not writing to alpha channel is expected
+ static void checkStates(GLboolean writeAlpha = GL_TRUE);
+
+protected:
+ static boost::unordered_map<LLGLenum, LLGLboolean> sStateMap;
+
+public:
+ enum { CURRENT_STATE = -2, DISABLED_STATE = 0, ENABLED_STATE = 1 };
+ LLGLState(LLGLenum state, S32 enabled = CURRENT_STATE);
+ ~LLGLState();
+ void setEnabled(S32 enabled);
+ void enable() { setEnabled(ENABLED_STATE); }
+ void disable() { setEnabled(DISABLED_STATE); }
+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);
+};
+
+// Enable with functor
+class LLGLEnableFunc : LLGLState
+{
+public:
+ LLGLEnableFunc(LLGLenum state, bool enable, boost::function<void()> func)
+ : LLGLState(state, enable)
+ {
+ if (enable)
+ {
+ func();
+ }
+ }
+};
+
+/// TODO: Being deprecated.
+class LLGLEnable : public LLGLState
+{
+public:
+ LLGLEnable(LLGLenum state) : LLGLState(state, ENABLED_STATE) {}
+};
+
+/// TODO: Being deprecated.
+class LLGLDisable : public LLGLState
+{
+public:
+ LLGLDisable(LLGLenum state) : LLGLState(state, DISABLED_STATE) {}
+};
+
+/*
+ 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, bool apply = true);
+ ~LLGLUserClipPlane();
+
+ void setPlane(F32 a, F32 b, F32 c, F32 d);
+ void disable();
+
+private:
+ bool mApply;
+
+ glh::matrix4f mProjection;
+ glh::matrix4f mModelview;
+};
+
+/*
+ Modify and load projection matrix to push depth values to far clip plane.
+
+ Restores projection matrix on destruction.
+ Saves/restores matrix mode around projection manipulation.
+ Does not stack.
+*/
+class LLGLSquashToFarClip
+{
+public:
+ LLGLSquashToFarClip();
+ LLGLSquashToFarClip(glh::matrix4f& projection, U32 layer = 0);
+
+ void setProjectionMatrix(glh::matrix4f& projection, U32 layer);
+
+ ~LLGLSquashToFarClip();
+};
+
+/*
+ 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;
+};
+
+const U32 FENCE_WAIT_TIME_NANOSECONDS = 1000; //1 ms
+
+class LLGLFence
+{
+public:
+ virtual ~LLGLFence()
+ {
+ }
+
+ virtual void placeFence() = 0;
+ virtual bool isCompleted() = 0;
+ virtual void wait() = 0;
+};
+
+class LLGLSyncFence : public LLGLFence
+{
+public:
+ GLsync mSync;
+
+ LLGLSyncFence();
+ virtual ~LLGLSyncFence();
+
+ void placeFence();
+ bool isCompleted();
+ void wait();
+};
+
+extern LLMatrix4 gGLObliqueProjectionInverse;
+
+#include "llglstates.h"
+
+void init_glstates();
+
+void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific, std::string* version_string );
+
+extern bool gClothRipple;
+extern bool gHeadlessClient;
+extern bool gNonInteractive;
+extern bool gGLActive;
+
+// Deal with changing glext.h definitions for newer SDK versions, specifically
+// with MAC OSX 10.5 -> 10.6
+
+
+#ifndef GL_DEPTH_ATTACHMENT
+#define GL_DEPTH_ATTACHMENT GL_DEPTH_ATTACHMENT_EXT
+#endif
+
+#ifndef GL_STENCIL_ATTACHMENT
+#define GL_STENCIL_ATTACHMENT GL_STENCIL_ATTACHMENT_EXT
+#endif
+
+#ifndef GL_FRAMEBUFFER
+#define GL_FRAMEBUFFER GL_FRAMEBUFFER_EXT
+#define GL_DRAW_FRAMEBUFFER GL_DRAW_FRAMEBUFFER_EXT
+#define GL_READ_FRAMEBUFFER GL_READ_FRAMEBUFFER_EXT
+#define GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_EXT
+#define GL_FRAMEBUFFER_UNSUPPORTED GL_FRAMEBUFFER_UNSUPPORTED_EXT
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT
+#define glGenFramebuffers glGenFramebuffersEXT
+#define glBindFramebuffer glBindFramebufferEXT
+#define glCheckFramebufferStatus glCheckFramebufferStatusEXT
+#define glBlitFramebuffer glBlitFramebufferEXT
+#define glDeleteFramebuffers glDeleteFramebuffersEXT
+#define glFramebufferRenderbuffer glFramebufferRenderbufferEXT
+#define glFramebufferTexture2D glFramebufferTexture2DEXT
+#endif
+
+#ifndef GL_RENDERBUFFER
+#define GL_RENDERBUFFER GL_RENDERBUFFER_EXT
+#define glGenRenderbuffers glGenRenderbuffersEXT
+#define glBindRenderbuffer glBindRenderbufferEXT
+#define glRenderbufferStorage glRenderbufferStorageEXT
+#define glRenderbufferStorageMultisample glRenderbufferStorageMultisampleEXT
+#define glDeleteRenderbuffers glDeleteRenderbuffersEXT
+#endif
+
+#ifndef GL_COLOR_ATTACHMENT
+#define GL_COLOR_ATTACHMENT GL_COLOR_ATTACHMENT_EXT
+#endif
+
+#ifndef GL_COLOR_ATTACHMENT0
+#define GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_EXT
+#endif
+
+#ifndef GL_COLOR_ATTACHMENT1
+#define GL_COLOR_ATTACHMENT1 GL_COLOR_ATTACHMENT1_EXT
+#endif
+
+#ifndef GL_COLOR_ATTACHMENT2
+#define GL_COLOR_ATTACHMENT2 GL_COLOR_ATTACHMENT2_EXT
+#endif
+
+#ifndef GL_COLOR_ATTACHMENT3
+#define GL_COLOR_ATTACHMENT3 GL_COLOR_ATTACHMENT3_EXT
+#endif
+
+
+#ifndef GL_DEPTH24_STENCIL8
+#define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_EXT
+#endif
+
+#endif // LL_LLGL_H
diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp
index 5c19931956..1651835b59 100644
--- a/indra/llrender/llglslshader.cpp
+++ b/indra/llrender/llglslshader.cpp
@@ -1,1952 +1,1952 @@
-/**
- * @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"
-#include "llvertexbuffer.h"
-#include "llrendertarget.h"
-
-#include "hbxxh.h"
-#include "llsdserialize.h"
-
-#if LL_DARWIN
-#include "OpenGL/OpenGL.h"
-#endif
-
- // Print-print list of shader included source files that are linked together via glAttachShader()
- // i.e. On macOS / OSX the AMD GLSL linker will display an error if a varying is left in an undefined state.
-#define DEBUG_SHADER_INCLUDES 0
-
-// 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;
-
-GLuint LLGLSLShader::sCurBoundShader = 0;
-LLGLSLShader* LLGLSLShader::sCurBoundShaderPtr = NULL;
-S32 LLGLSLShader::sIndexedTextureChannels = 0;
-bool LLGLSLShader::sProfileEnabled = false;
-std::set<LLGLSLShader*> LLGLSLShader::sInstances;
-LLGLSLShader::defines_map_t LLGLSLShader::sGlobalDefines;
-U64 LLGLSLShader::sTotalTimeElapsed = 0;
-U32 LLGLSLShader::sTotalTrianglesDrawn = 0;
-U64 LLGLSLShader::sTotalSamplesDrawn = 0;
-U32 LLGLSLShader::sTotalBinds = 0;
-
-//UI shader -- declared here so llui_libtest will link properly
-LLGLSLShader gUIProgram;
-LLGLSLShader gSolidColorProgram;
-
-// NOTE: Keep gShaderConsts* and LLGLSLShader::ShaderConsts_e in sync!
-const std::string gShaderConstsKey[LLGLSLShader::NUM_SHADER_CONSTS] =
-{
- "LL_SHADER_CONST_CLOUD_MOON_DEPTH"
- , "LL_SHADER_CONST_STAR_DEPTH"
-};
-
-// NOTE: Keep gShaderConsts* and LLGLSLShader::ShaderConsts_e in sync!
-const std::string gShaderConstsVal[LLGLSLShader::NUM_SHADER_CONSTS] =
-{
- "0.99998" // SHADER_CONST_CLOUD_MOON_DEPTH // SL-14113
- , "0.99999" // SHADER_CONST_STAR_DEPTH // SL-14113
-};
-
-
-bool shouldChange(const LLVector4& v1, const LLVector4& v2)
-{
- return v1 != v2;
-}
-
-//===============================
-// LLGLSL Shader implementation
-//===============================
-
-//static
-void LLGLSLShader::initProfile()
-{
- sProfileEnabled = true;
- sTotalTimeElapsed = 0;
- sTotalTrianglesDrawn = 0;
- sTotalSamplesDrawn = 0;
- sTotalBinds = 0;
-
- for (std::set<LLGLSLShader*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
- {
- (*iter)->clearStats();
- }
-}
-
-
-struct LLGLSLShaderCompareTimeElapsed
-{
- bool operator()(const LLGLSLShader* const& lhs, const LLGLSLShader* const& rhs)
- {
- return lhs->mTimeElapsed < rhs->mTimeElapsed;
- }
-};
-
-//static
-void LLGLSLShader::finishProfile(bool emit_report)
-{
- sProfileEnabled = false;
-
- if (emit_report)
- {
- std::vector<LLGLSLShader*> sorted;
-
- for (std::set<LLGLSLShader*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
- {
- sorted.push_back(*iter);
- }
-
- std::sort(sorted.begin(), sorted.end(), LLGLSLShaderCompareTimeElapsed());
-
- bool unbound = false;
- for (std::vector<LLGLSLShader*>::iterator iter = sorted.begin(); iter != sorted.end(); ++iter)
- {
- (*iter)->dumpStats();
- if ((*iter)->mBinds == 0)
- {
- unbound = true;
- }
- }
-
- LL_INFOS() << "-----------------------------------" << LL_ENDL;
- LL_INFOS() << "Total rendering time: " << llformat("%.4f ms", sTotalTimeElapsed / 1000000.f) << LL_ENDL;
- LL_INFOS() << "Total samples drawn: " << llformat("%.4f million", sTotalSamplesDrawn / 1000000.f) << LL_ENDL;
- LL_INFOS() << "Total triangles drawn: " << llformat("%.3f million", sTotalTrianglesDrawn / 1000000.f) << LL_ENDL;
- LL_INFOS() << "-----------------------------------" << LL_ENDL;
-
- if (unbound)
- {
- LL_INFOS() << "The following shaders were unused: " << LL_ENDL;
- for (std::vector<LLGLSLShader*>::iterator iter = sorted.begin(); iter != sorted.end(); ++iter)
- {
- if ((*iter)->mBinds == 0)
- {
- LL_INFOS() << (*iter)->mName << LL_ENDL;
- }
- }
- }
- }
-}
-
-void LLGLSLShader::clearStats()
-{
- mTrianglesDrawn = 0;
- mTimeElapsed = 0;
- mSamplesDrawn = 0;
- mBinds = 0;
-}
-
-void LLGLSLShader::dumpStats()
-{
- if (mBinds > 0)
- {
- LL_INFOS() << "=============================================" << LL_ENDL;
- LL_INFOS() << mName << LL_ENDL;
- for (U32 i = 0; i < mShaderFiles.size(); ++i)
- {
- LL_INFOS() << mShaderFiles[i].first << LL_ENDL;
- }
- LL_INFOS() << "=============================================" << LL_ENDL;
-
- F32 ms = mTimeElapsed / 1000000.f;
- F32 seconds = ms / 1000.f;
-
- F32 pct_tris = (F32)mTrianglesDrawn / (F32)sTotalTrianglesDrawn * 100.f;
- F32 tris_sec = (F32)(mTrianglesDrawn / 1000000.0);
- tris_sec /= seconds;
-
- F32 pct_samples = (F32)((F64)mSamplesDrawn / (F64)sTotalSamplesDrawn) * 100.f;
- F32 samples_sec = (F32)mSamplesDrawn / 1000000000.0;
- samples_sec /= seconds;
-
- F32 pct_binds = (F32)mBinds / (F32)sTotalBinds * 100.f;
-
- LL_INFOS() << "Triangles Drawn: " << mTrianglesDrawn << " " << llformat("(%.2f pct of total, %.3f million/sec)", pct_tris, tris_sec) << LL_ENDL;
- LL_INFOS() << "Binds: " << mBinds << " " << llformat("(%.2f pct of total)", pct_binds) << LL_ENDL;
- LL_INFOS() << "SamplesDrawn: " << mSamplesDrawn << " " << llformat("(%.2f pct of total, %.3f billion/sec)", pct_samples, samples_sec) << LL_ENDL;
- LL_INFOS() << "Time Elapsed: " << mTimeElapsed << " " << llformat("(%.2f pct of total, %.5f ms)\n", (F32)((F64)mTimeElapsed / (F64)sTotalTimeElapsed) * 100.f, ms) << LL_ENDL;
- }
-}
-
-//static
-void LLGLSLShader::startProfile()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- if (sProfileEnabled && sCurBoundShaderPtr)
- {
- sCurBoundShaderPtr->placeProfileQuery();
- }
-
-}
-
-//static
-void LLGLSLShader::stopProfile()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- if (sProfileEnabled && sCurBoundShaderPtr)
- {
- sCurBoundShaderPtr->unbind();
- }
-}
-
-void LLGLSLShader::placeProfileQuery(bool for_runtime)
-{
- if (sProfileEnabled || for_runtime)
- {
- if (mTimerQuery == 0)
- {
- glGenQueries(1, &mSamplesQuery);
- glGenQueries(1, &mTimerQuery);
- glGenQueries(1, &mPrimitivesQuery);
- }
-
- glBeginQuery(GL_TIME_ELAPSED, mTimerQuery);
-
- if (!for_runtime)
- {
- glBeginQuery(GL_SAMPLES_PASSED, mSamplesQuery);
- glBeginQuery(GL_PRIMITIVES_GENERATED, mPrimitivesQuery);
- }
- }
-}
-
-bool LLGLSLShader::readProfileQuery(bool for_runtime, bool force_read)
-{
- if (sProfileEnabled || for_runtime)
- {
- if (!mProfilePending)
- {
- glEndQuery(GL_TIME_ELAPSED);
- if (!for_runtime)
- {
- glEndQuery(GL_SAMPLES_PASSED);
- glEndQuery(GL_PRIMITIVES_GENERATED);
- }
- mProfilePending = for_runtime;
- }
-
- if (mProfilePending && for_runtime && !force_read)
- {
- GLuint64 result = 0;
- glGetQueryObjectui64v(mTimerQuery, GL_QUERY_RESULT_AVAILABLE, &result);
-
- if (result != GL_TRUE)
- {
- return false;
- }
- }
-
- GLuint64 time_elapsed = 0;
- glGetQueryObjectui64v(mTimerQuery, GL_QUERY_RESULT, &time_elapsed);
- mTimeElapsed += time_elapsed;
- mProfilePending = false;
-
- if (!for_runtime)
- {
- GLuint64 samples_passed = 0;
- glGetQueryObjectui64v(mSamplesQuery, GL_QUERY_RESULT, &samples_passed);
-
- U64 primitives_generated = 0;
- glGetQueryObjectui64v(mPrimitivesQuery, GL_QUERY_RESULT, &primitives_generated);
- sTotalTimeElapsed += time_elapsed;
-
- sTotalSamplesDrawn += samples_passed;
- mSamplesDrawn += samples_passed;
-
- U32 tri_count = (U32)primitives_generated / 3;
-
- mTrianglesDrawn += tri_count;
- sTotalTrianglesDrawn += tri_count;
-
- sTotalBinds++;
- mBinds++;
- }
- }
-
- return true;
-}
-
-
-
-LLGLSLShader::LLGLSLShader()
- : mProgramObject(0),
- mAttributeMask(0),
- mTotalUniformSize(0),
- mActiveTextureChannels(0),
- mShaderLevel(0),
- mShaderGroup(SG_DEFAULT),
- mFeatures(),
- mUniformsDirty(false),
- mTimerQuery(0),
- mSamplesQuery(0),
- mPrimitivesQuery(0)
-{
-
-}
-
-LLGLSLShader::~LLGLSLShader()
-{
-}
-
-void LLGLSLShader::unload()
-{
- mShaderFiles.clear();
- mDefines.clear();
- mFeatures = LLShaderFeatures();
-
- unloadInternal();
-}
-
-void LLGLSLShader::unloadInternal()
-{
- sInstances.erase(this);
-
- stop_glerror();
- mAttribute.clear();
- mTexture.clear();
- mUniform.clear();
-
- if (mProgramObject)
- {
- GLuint obj[1024];
- GLsizei count = 0;
- glGetAttachedShaders(mProgramObject, 1024, &count, obj);
-
- for (GLsizei i = 0; i < count; i++)
- {
- glDetachShader(mProgramObject, obj[i]);
- }
-
- for (GLsizei i = 0; i < count; i++)
- {
- if (glIsShader(obj[i]))
- {
- glDeleteShader(obj[i]);
- }
- }
-
- glDeleteProgram(mProgramObject);
-
- mProgramObject = 0;
- }
-
- if (mTimerQuery)
- {
- glDeleteQueries(1, &mTimerQuery);
- mTimerQuery = 0;
- }
-
- if (mSamplesQuery)
- {
- glDeleteQueries(1, &mSamplesQuery);
- mSamplesQuery = 0;
- }
-
- //hack to make apple not complain
- glGetError();
-
- stop_glerror();
-}
-
-bool LLGLSLShader::createShader(std::vector<LLStaticHashedString>* attributes,
- std::vector<LLStaticHashedString>* uniforms,
- U32 varying_count,
- const char** varyings)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- unloadInternal();
-
- sInstances.insert(this);
-
- //reloading, reset matrix hash values
- for (U32 i = 0; i < LLRender::NUM_MATRIX_MODES; ++i)
- {
- mMatHash[i] = 0xFFFFFFFF;
- }
- mLightHash = 0xFFFFFFFF;
-
- llassert_always(!mShaderFiles.empty());
-
-#if LL_DARWIN
- // work-around missing mix(vec3,vec3,bvec3)
- mDefines["OLD_SELECT"] = "1";
-#endif
-
- mShaderHash = hash();
-
- // Create program
- mProgramObject = glCreateProgram();
- if (mProgramObject == 0)
- {
- // Shouldn't happen if shader related extensions, like ARB_vertex_shader, exist.
- LL_SHADER_LOADING_WARNS() << "Failed to create handle for shader: " << mName << LL_ENDL;
- unloadInternal();
- return false;
- }
-
- bool success = true;
-
- mUsingBinaryProgram = LLShaderMgr::instance()->loadCachedProgramBinary(this);
-
- if (!mUsingBinaryProgram)
- {
-#if DEBUG_SHADER_INCLUDES
- fprintf(stderr, "--- %s ---\n", mName.c_str());
-#endif // DEBUG_SHADER_INCLUDES
-
- //compile new source
- vector< pair<string, GLenum> >::iterator fileIter = mShaderFiles.begin();
- for (; fileIter != mShaderFiles.end(); fileIter++)
- {
- GLuint shaderhandle = LLShaderMgr::instance()->loadShaderFile((*fileIter).first, mShaderLevel, (*fileIter).second, &mDefines, mFeatures.mIndexedTextureChannels);
- LL_DEBUGS("ShaderLoading") << "SHADER FILE: " << (*fileIter).first << " mShaderLevel=" << mShaderLevel << LL_ENDL;
- if (shaderhandle)
- {
- attachObject(shaderhandle);
- }
- else
- {
- success = false;
- }
- }
- }
-
- // Attach existing objects
- if (!LLShaderMgr::instance()->attachShaderFeatures(this))
- {
- unloadInternal();
- return false;
- }
- // Map attributes and uniforms
- if (success)
- {
- success = mapAttributes(attributes);
- }
- if (success)
- {
- success = mapUniforms(uniforms);
- }
- if (!success)
- {
- LL_SHADER_LOADING_WARNS() << "Failed to link shader: " << mName << LL_ENDL;
-
- // Try again using a lower shader level;
- if (mShaderLevel > 0)
- {
- LL_SHADER_LOADING_WARNS() << "Failed to link using shader level " << mShaderLevel << " trying again using shader level " << (mShaderLevel - 1) << LL_ENDL;
- mShaderLevel--;
- return createShader(attributes, uniforms);
- }
- else
- {
- // Give up and unload shader.
- unloadInternal();
- }
- }
- else if (mFeatures.mIndexedTextureChannels > 0)
- { //override texture channels for indexed texture rendering
- bind();
- S32 channel_count = mFeatures.mIndexedTextureChannels;
-
- for (S32 i = 0; i < channel_count; i++)
- {
- LLStaticHashedString uniName(llformat("tex%d", i));
- uniform1i(uniName, i);
- }
-
- S32 cur_tex = channel_count; //adjust any texture channels that might have been overwritten
- for (U32 i = 0; i < mTexture.size(); i++)
- {
- if (mTexture[i] > -1 && mTexture[i] < channel_count)
- {
- llassert(cur_tex < gGLManager.mNumTextureImageUnits);
- uniform1i(i, cur_tex);
- mTexture[i] = cur_tex++;
- }
- }
- unbind();
- }
-
-#ifdef LL_PROFILER_ENABLE_RENDER_DOC
- setLabel(mName.c_str());
-#endif
-
- return success;
-}
-
-#if DEBUG_SHADER_INCLUDES
-void dumpAttachObject(const char* func_name, GLuint program_object, const std::string& object_path)
-{
- GLchar* info_log;
- GLint info_len_expect = 0;
- GLint info_len_actual = 0;
-
- glGetShaderiv(program_object, GL_INFO_LOG_LENGTH, , &info_len_expect);
- fprintf(stderr, " * %-20s(), log size: %d, %s\n", func_name, info_len_expect, object_path.c_str());
-
- if (info_len_expect > 0)
- {
- fprintf(stderr, " ========== %s() ========== \n", func_name);
- info_log = new GLchar[info_len_expect];
- glGetProgramInfoLog(program_object, info_len_expect, &info_len_actual, info_log);
- fprintf(stderr, "%s\n", info_log);
- delete[] info_log;
- }
-}
-#endif // DEBUG_SHADER_INCLUDES
-
-bool LLGLSLShader::attachVertexObject(std::string object_path)
-{
- if (LLShaderMgr::instance()->mVertexShaderObjects.count(object_path) > 0)
- {
- stop_glerror();
- glAttachShader(mProgramObject, LLShaderMgr::instance()->mVertexShaderObjects[object_path]);
-#if DEBUG_SHADER_INCLUDES
- dumpAttachObject("attachVertexObject", mProgramObject, object_path);
-#endif // DEBUG_SHADER_INCLUDES
- stop_glerror();
- return true;
- }
- else
- {
- LL_SHADER_LOADING_WARNS() << "Attempting to attach shader object: '" << object_path << "' that hasn't been compiled." << LL_ENDL;
- return false;
- }
-}
-
-bool LLGLSLShader::attachFragmentObject(std::string object_path)
-{
- if(mUsingBinaryProgram)
- return true;
-
- if (LLShaderMgr::instance()->mFragmentShaderObjects.count(object_path) > 0)
- {
- stop_glerror();
- glAttachShader(mProgramObject, LLShaderMgr::instance()->mFragmentShaderObjects[object_path]);
-#if DEBUG_SHADER_INCLUDES
- dumpAttachObject("attachFragmentObject", mProgramObject, object_path);
-#endif // DEBUG_SHADER_INCLUDES
- stop_glerror();
- return true;
- }
- else
- {
- LL_SHADER_LOADING_WARNS() << "Attempting to attach shader object: '" << object_path << "' that hasn't been compiled." << LL_ENDL;
- return false;
- }
-}
-
-void LLGLSLShader::attachObject(GLuint object)
-{
- if(mUsingBinaryProgram)
- return;
-
- if (object != 0)
- {
- stop_glerror();
- glAttachShader(mProgramObject, object);
-#if DEBUG_SHADER_INCLUDES
- std::string object_path("???");
- dumpAttachObject("attachObject", mProgramObject, object_path);
-#endif // DEBUG_SHADER_INCLUDES
- stop_glerror();
- }
- else
- {
- LL_SHADER_LOADING_WARNS() << "Attempting to attach non existing shader object. " << LL_ENDL;
- }
-}
-
-void LLGLSLShader::attachObjects(GLuint* objects, S32 count)
-{
- if(mUsingBinaryProgram)
- return;
-
- for (S32 i = 0; i < count; i++)
- {
- attachObject(objects[i]);
- }
-}
-
-bool LLGLSLShader::mapAttributes(const std::vector<LLStaticHashedString>* attributes)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- bool res = true;
- if (!mUsingBinaryProgram)
- {
- //before linking, make sure reserved attributes always have consistent locations
- for (U32 i = 0; i < LLShaderMgr::instance()->mReservedAttribs.size(); i++)
- {
- const char* name = LLShaderMgr::instance()->mReservedAttribs[i].c_str();
- glBindAttribLocation(mProgramObject, i, (const GLchar*)name);
- }
-
- //link the program
- res = link();
- }
-
- mAttribute.clear();
- U32 numAttributes = (attributes == NULL) ? 0 : attributes->size();
-#if LL_RELEASE_WITH_DEBUG_INFO
- mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, { -1, NULL });
-#else
- mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, -1);
-#endif
-
- if (res)
- { //read back channel locations
-
- mAttributeMask = 0;
-
- //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 = glGetAttribLocation(mProgramObject, (const GLchar*)name);
- if (index != -1)
- {
-#if LL_RELEASE_WITH_DEBUG_INFO
- mAttribute[i] = { index, name };
-#else
- mAttribute[i] = index;
-#endif
- mAttributeMask |= 1 << i;
- LL_DEBUGS("ShaderUniform") << "Attribute " << name << " assigned to channel " << index << LL_ENDL;
- }
- }
- if (attributes != NULL)
- {
- for (U32 i = 0; i < numAttributes; i++)
- {
- const char* name = (*attributes)[i].String().c_str();
- S32 index = glGetAttribLocation(mProgramObject, name);
- if (index != -1)
- {
- mAttribute[LLShaderMgr::instance()->mReservedAttribs.size() + i] = index;
- LL_DEBUGS("ShaderUniform") << "Attribute " << name << " assigned to channel " << index << LL_ENDL;
- }
- }
- }
-
- return true;
- }
-
- return false;
-}
-
-void LLGLSLShader::mapUniform(GLint index, const vector<LLStaticHashedString>* uniforms)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- if (index == -1)
- {
- return;
- }
-
- GLenum type;
- GLsizei length;
- GLint size = -1;
- char name[1024]; /* Flawfinder: ignore */
- name[0] = 0;
-
-
- glGetActiveUniform(mProgramObject, index, 1024, &length, &size, &type, (GLchar*)name);
- if (size > 0)
- {
- switch (type)
- {
- case GL_FLOAT_VEC2: size *= 2; break;
- case GL_FLOAT_VEC3: size *= 3; break;
- case GL_FLOAT_VEC4: size *= 4; break;
- case GL_DOUBLE: size *= 2; break;
- case GL_DOUBLE_VEC2: size *= 2; break;
- case GL_DOUBLE_VEC3: size *= 6; break;
- case GL_DOUBLE_VEC4: size *= 8; break;
- case GL_INT_VEC2: size *= 2; break;
- case GL_INT_VEC3: size *= 3; break;
- case GL_INT_VEC4: size *= 4; break;
- case GL_UNSIGNED_INT_VEC2: size *= 2; break;
- case GL_UNSIGNED_INT_VEC3: size *= 3; break;
- case GL_UNSIGNED_INT_VEC4: size *= 4; break;
- case GL_BOOL_VEC2: size *= 2; break;
- case GL_BOOL_VEC3: size *= 3; break;
- case GL_BOOL_VEC4: size *= 4; break;
- case GL_FLOAT_MAT2: size *= 4; break;
- case GL_FLOAT_MAT3: size *= 9; break;
- case GL_FLOAT_MAT4: size *= 16; break;
- case GL_FLOAT_MAT2x3: size *= 6; break;
- case GL_FLOAT_MAT2x4: size *= 8; break;
- case GL_FLOAT_MAT3x2: size *= 6; break;
- case GL_FLOAT_MAT3x4: size *= 12; break;
- case GL_FLOAT_MAT4x2: size *= 8; break;
- case GL_FLOAT_MAT4x3: size *= 12; break;
- case GL_DOUBLE_MAT2: size *= 8; break;
- case GL_DOUBLE_MAT3: size *= 18; break;
- case GL_DOUBLE_MAT4: size *= 32; break;
- case GL_DOUBLE_MAT2x3: size *= 12; break;
- case GL_DOUBLE_MAT2x4: size *= 16; break;
- case GL_DOUBLE_MAT3x2: size *= 12; break;
- case GL_DOUBLE_MAT3x4: size *= 24; break;
- case GL_DOUBLE_MAT4x2: size *= 16; break;
- case GL_DOUBLE_MAT4x3: size *= 24; break;
- }
- mTotalUniformSize += size;
- }
-
- S32 location = glGetUniformLocation(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;
- }
-
- LLStaticHashedString hashedName(name);
- mUniformMap[hashedName] = location;
-
- LL_DEBUGS("ShaderUniform") << "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] == name))
- {
- //found it
- mUniform[i] = location;
- mTexture[i] = mapUniformTextureChannel(location, type, size);
- return;
- }
- }
-
- if (uniforms != NULL)
- {
- for (U32 i = 0; i < uniforms->size(); i++)
- {
- if ((mUniform[i + LLShaderMgr::instance()->mReservedUniforms.size()] == -1)
- && ((*uniforms)[i].String() == name))
- {
- //found it
- mUniform[i + LLShaderMgr::instance()->mReservedUniforms.size()] = location;
- mTexture[i + LLShaderMgr::instance()->mReservedUniforms.size()] = mapUniformTextureChannel(location, type, size);
- return;
- }
- }
- }
- }
-}
-
-void LLGLSLShader::clearPermutations()
-{
- mDefines.clear();
-}
-
-void LLGLSLShader::addPermutation(std::string name, std::string value)
-{
- mDefines[name] = value;
-}
-
-void LLGLSLShader::addConstant(const LLGLSLShader::eShaderConsts shader_const)
-{
- addPermutation(gShaderConstsKey[shader_const], gShaderConstsVal[shader_const]);
-}
-
-void LLGLSLShader::removePermutation(std::string name)
-{
- mDefines.erase(name);
-}
-
-GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type, GLint size)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- if ((type >= GL_SAMPLER_1D && type <= GL_SAMPLER_2D_RECT_SHADOW) ||
- type == GL_SAMPLER_2D_MULTISAMPLE ||
- type == GL_SAMPLER_CUBE_MAP_ARRAY)
- { //this here is a texture
- GLint ret = mActiveTextureChannels;
- if (size == 1)
- {
- glUniform1i(location, mActiveTextureChannels);
- LL_DEBUGS("ShaderUniform") << "Assigned to texture channel " << mActiveTextureChannels << LL_ENDL;
- mActiveTextureChannels++;
- }
- else
- {
- //is array of textures, make sequential after this texture
- GLint channel[32]; // <=== only support up to 32 texture channels
- llassert(size <= 32);
- size = llmin(size, 32);
- for (int i = 0; i < size; ++i)
- {
- channel[i] = mActiveTextureChannels++;
- }
- glUniform1iv(location, size, channel);
- LL_DEBUGS("ShaderUniform") << "Assigned to texture channel " <<
- (mActiveTextureChannels - size) << " through " << (mActiveTextureChannels - 1) << LL_ENDL;
- }
-
- llassert(mActiveTextureChannels <= 32); // too many textures (probably)
- return ret;
- }
- return -1;
-}
-
-bool LLGLSLShader::mapUniforms(const vector<LLStaticHashedString>* uniforms)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- bool res = true;
-
- mTotalUniformSize = 0;
- 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;
- glGetProgramiv(mProgramObject, GL_ACTIVE_UNIFORMS, &activeCount);
-
- //........................................................................................................................................
- //........................................................................................
-
- /*
- EXPLANATION:
- This is part of code is temporary because as the final result the mapUniform() should be rewrited.
- But it's a huge a volume of work which is need to be a more carefully performed for avoid possible
- regression's (i.e. it should be formalized a separate ticket in JIRA).
-
- RESON:
- The reason of this code is that SL engine is very sensitive to fact that "diffuseMap" should be appear
- first as uniform parameter which is should get 0-"texture channel" index (see mapUniformTextureChannel() and mActiveTextureChannels)
- it influence to which is texture matrix will be updated during rendering.
-
- But, order of indexe's of uniform variables is not defined and GLSL compiler can change it as want
- , even if the "diffuseMap" will be appear and use first in shader code.
-
- As example where this situation appear see: "Deferred Material Shader 28/29/30/31"
- And tickets: MAINT-4165, MAINT-4839, MAINT-3568, MAINT-6437
-
- --- davep TODO -- pretty sure the entire block here is superstitious and that the uniform index has nothing to do with the texture channel
- texture channel should follow the uniform VALUE
- */
-
-
- S32 diffuseMap = glGetUniformLocation(mProgramObject, "diffuseMap");
- S32 specularMap = glGetUniformLocation(mProgramObject, "specularMap");
- S32 bumpMap = glGetUniformLocation(mProgramObject, "bumpMap");
- S32 altDiffuseMap = glGetUniformLocation(mProgramObject, "altDiffuseMap");
- S32 environmentMap = glGetUniformLocation(mProgramObject, "environmentMap");
- S32 reflectionMap = glGetUniformLocation(mProgramObject, "reflectionMap");
-
- std::set<S32> skip_index;
-
- if (-1 != diffuseMap && (-1 != specularMap || -1 != bumpMap || -1 != environmentMap || -1 != altDiffuseMap))
- {
- GLenum type;
- GLsizei length;
- GLint size = -1;
- char name[1024];
-
- diffuseMap = altDiffuseMap = specularMap = bumpMap = environmentMap = -1;
-
- for (S32 i = 0; i < activeCount; i++)
- {
- name[0] = '\0';
-
- glGetActiveUniform(mProgramObject, i, 1024, &length, &size, &type, (GLchar*)name);
-
- if (-1 == diffuseMap && std::string(name) == "diffuseMap")
- {
- diffuseMap = i;
- continue;
- }
-
- if (-1 == specularMap && std::string(name) == "specularMap")
- {
- specularMap = i;
- continue;
- }
-
- if (-1 == bumpMap && std::string(name) == "bumpMap")
- {
- bumpMap = i;
- continue;
- }
-
- if (-1 == environmentMap && std::string(name) == "environmentMap")
- {
- environmentMap = i;
- continue;
- }
-
- if (-1 == reflectionMap && std::string(name) == "reflectionMap")
- {
- reflectionMap = i;
- continue;
- }
-
- if (-1 == altDiffuseMap && std::string(name) == "altDiffuseMap")
- {
- altDiffuseMap = i;
- continue;
- }
- }
-
- bool specularDiff = specularMap < diffuseMap && -1 != specularMap;
- bool bumpLessDiff = bumpMap < diffuseMap && -1 != bumpMap;
- bool envLessDiff = environmentMap < diffuseMap && -1 != environmentMap;
- bool refLessDiff = reflectionMap < diffuseMap && -1 != reflectionMap;
-
- if (specularDiff || bumpLessDiff || envLessDiff || refLessDiff)
- {
- mapUniform(diffuseMap, uniforms);
- skip_index.insert(diffuseMap);
-
- if (-1 != specularMap) {
- mapUniform(specularMap, uniforms);
- skip_index.insert(specularMap);
- }
-
- if (-1 != bumpMap) {
- mapUniform(bumpMap, uniforms);
- skip_index.insert(bumpMap);
- }
-
- if (-1 != environmentMap) {
- mapUniform(environmentMap, uniforms);
- skip_index.insert(environmentMap);
- }
-
- if (-1 != reflectionMap) {
- mapUniform(reflectionMap, uniforms);
- skip_index.insert(reflectionMap);
- }
- }
- }
-
- //........................................................................................
-
- for (S32 i = 0; i < activeCount; i++)
- {
- //........................................................................................
- if (skip_index.end() != skip_index.find(i)) continue;
- //........................................................................................
-
- mapUniform(i, uniforms);
- }
- //........................................................................................................................................
-
- if (mFeatures.hasReflectionProbes) // Set up block binding, in a way supported by Apple (rather than binding = 1 in .glsl).
- { // See slide 35 and more of https://docs.huihoo.com/apple/wwdc/2011/session_420__advances_in_opengl_for_mac_os_x_lion.pdf
- static const GLuint BLOCKBINDING = 1; //picked by us
- //Get the index, similar to a uniform location
- GLuint UBOBlockIndex = glGetUniformBlockIndex(mProgramObject, "ReflectionProbes");
- if (UBOBlockIndex != GL_INVALID_INDEX)
- {
- //Set this index to a binding index
- glUniformBlockBinding(mProgramObject, UBOBlockIndex, BLOCKBINDING);
- }
- }
- unbind();
-
- LL_DEBUGS("ShaderUniform") << "Total Uniform Size: " << mTotalUniformSize << LL_ENDL;
- return res;
-}
-
-
-bool LLGLSLShader::link(bool suppress_errors)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- bool success = LLShaderMgr::instance()->linkProgramObject(mProgramObject, suppress_errors);
-
- if (!success && !suppress_errors)
- {
- LLShaderMgr::instance()->dumpObjectLog(mProgramObject, !success, mName);
- }
-
- if (success)
- {
- LLShaderMgr::instance()->saveCachedProgramBinary(this);
- }
-
- return success;
-}
-
-void LLGLSLShader::bind()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- llassert(mProgramObject != 0);
-
- gGL.flush();
-
- if (sCurBoundShader != mProgramObject) // Don't re-bind current shader
- {
- if (sCurBoundShaderPtr)
- {
- sCurBoundShaderPtr->readProfileQuery();
- }
- LLVertexBuffer::unbind();
- glUseProgram(mProgramObject);
- sCurBoundShader = mProgramObject;
- sCurBoundShaderPtr = this;
- placeProfileQuery();
- LLVertexBuffer::setupClientArrays(mAttributeMask);
- }
-
- if (mUniformsDirty)
- {
- LLShaderMgr::instance()->updateShaderUniforms(this);
- mUniformsDirty = false;
- }
-}
-
-void LLGLSLShader::bind(bool rigged)
-{
- if (rigged)
- {
- llassert(mRiggedVariant);
- mRiggedVariant->bind();
- }
- else
- {
- bind();
- }
-}
-
-void LLGLSLShader::unbind(void)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- gGL.flush();
- LLVertexBuffer::unbind();
-
- if (sCurBoundShaderPtr)
- {
- sCurBoundShaderPtr->readProfileQuery();
- }
-
- glUseProgram(0);
- sCurBoundShader = 0;
- sCurBoundShaderPtr = NULL;
-}
-
-S32 LLGLSLShader::bindTexture(const std::string& uniform, LLTexture* texture, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace colorspace)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- S32 channel = 0;
- channel = getUniformLocation(uniform);
-
- return bindTexture(channel, texture, mode, colorspace);
-}
-
-S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace colorspace)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- if (uniform < 0 || uniform >= (S32)mTexture.size())
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL;
- return -1;
- }
-
- uniform = mTexture[uniform];
-
- if (uniform > -1)
- {
- gGL.getTexUnit(uniform)->bindFast(texture);
- gGL.getTexUnit(uniform)->setTextureColorSpace(colorspace);
- }
-
- return uniform;
-}
-
-S32 LLGLSLShader::bindTexture(S32 uniform, LLRenderTarget* texture, bool depth, LLTexUnit::eTextureFilterOptions mode, U32 index)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- if (uniform < 0 || uniform >= (S32)mTexture.size())
- {
- return -1;
- }
-
- uniform = getTextureChannel(uniform);
-
- if (uniform > -1)
- {
- if (depth) {
- gGL.getTexUnit(uniform)->bind(texture, true);
- }
- else {
- bool has_mips = mode == LLTexUnit::TFO_TRILINEAR || mode == LLTexUnit::TFO_ANISOTROPIC;
- gGL.getTexUnit(uniform)->bindManual(texture->getUsage(), texture->getTexture(index), has_mips);
- }
-
- gGL.getTexUnit(uniform)->setTextureFilteringOption(mode);
- }
-
- return uniform;
-}
-
-S32 LLGLSLShader::bindTexture(const std::string& uniform, LLRenderTarget* texture, bool depth, LLTexUnit::eTextureFilterOptions mode)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- S32 channel = 0;
- channel = getUniformLocation(uniform);
-
- return bindTexture(channel, texture, depth, mode);
-}
-
-S32 LLGLSLShader::unbindTexture(const std::string& uniform, LLTexUnit::eTextureType mode)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- S32 channel = 0;
- channel = getUniformLocation(uniform);
-
- return unbindTexture(channel);
-}
-
-S32 LLGLSLShader::unbindTexture(S32 uniform, LLTexUnit::eTextureType mode)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- if (uniform < 0 || uniform >= (S32)mTexture.size())
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL;
- return -1;
- }
-
- uniform = mTexture[uniform];
-
- if (uniform > -1)
- {
- gGL.getTexUnit(uniform)->unbindFast(mode);
- }
-
- return uniform;
-}
-
-S32 LLGLSLShader::getTextureChannel(S32 uniform) const
-{
- return mTexture[uniform];
-}
-
-S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace space)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- if (uniform < 0 || uniform >= (S32)mTexture.size())
- {
- LL_SHADER_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);
- gGL.getTexUnit(index)->setTextureColorSpace(space);
- }
- return index;
-}
-
-S32 LLGLSLShader::disableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace space)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- if (uniform < 0 || uniform >= (S32)mTexture.size())
- {
- LL_SHADER_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 && gGL.getTexUnit(index)->getCurrColorSpace() != space)
- {
- if (gDebugSession)
- {
- gFailLog << "Texture channel " << index << " texture type corrupted." << std::endl;
- ll_fail("LLGLSLShader::disableTexture failed");
- }
- else
- {
- LL_ERRS() << "Texture channel " << index << " texture type corrupted." << LL_ENDL;
- }
- }
- gGL.getTexUnit(index)->disable();
- }
- return index;
-}
-
-void LLGLSLShader::uniform1i(U32 index, GLint x)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- const auto& iter = mValue.find(mUniform[index]);
- if (iter == mValue.end() || iter->second.mV[0] != x)
- {
- glUniform1i(mUniform[index], x);
- mValue[mUniform[index]] = LLVector4(x, 0.f, 0.f, 0.f);
- }
- }
- }
-}
-
-void LLGLSLShader::uniform1f(U32 index, GLfloat x)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- const auto& iter = mValue.find(mUniform[index]);
- if (iter == mValue.end() || iter->second.mV[0] != x)
- {
- glUniform1f(mUniform[index], x);
- mValue[mUniform[index]] = LLVector4(x, 0.f, 0.f, 0.f);
- }
- }
- }
-}
-
-void LLGLSLShader::fastUniform1f(U32 index, GLfloat x)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
- llassert(mProgramObject);
- llassert(mUniform.size() <= index);
- llassert(mUniform[index] >= 0);
- glUniform1f(mUniform[index], x);
-}
-
-void LLGLSLShader::uniform2f(U32 index, GLfloat x, GLfloat y)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- const auto& iter = mValue.find(mUniform[index]);
- LLVector4 vec(x, y, 0.f, 0.f);
- if (iter == mValue.end() || shouldChange(iter->second, vec))
- {
- glUniform2f(mUniform[index], x, y);
- mValue[mUniform[index]] = vec;
- }
- }
- }
-}
-
-void LLGLSLShader::uniform3f(U32 index, GLfloat x, GLfloat y, GLfloat z)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- const auto& iter = mValue.find(mUniform[index]);
- LLVector4 vec(x, y, z, 0.f);
- if (iter == mValue.end() || shouldChange(iter->second, vec))
- {
- glUniform3f(mUniform[index], x, y, z);
- mValue[mUniform[index]] = vec;
- }
- }
- }
-}
-
-void LLGLSLShader::uniform4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- const auto& iter = mValue.find(mUniform[index]);
- LLVector4 vec(x, y, z, w);
- if (iter == mValue.end() || shouldChange(iter->second, vec))
- {
- glUniform4f(mUniform[index], x, y, z, w);
- mValue[mUniform[index]] = vec;
- }
- }
- }
-}
-
-void LLGLSLShader::uniform1iv(U32 index, U32 count, const GLint* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- const auto& 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)
- {
- glUniform1iv(mUniform[index], count, v);
- mValue[mUniform[index]] = vec;
- }
- }
- }
-}
-
-void LLGLSLShader::uniform4iv(U32 index, U32 count, const GLint* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- const auto& 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)
- {
- glUniform1iv(mUniform[index], count, v);
- mValue[mUniform[index]] = vec;
- }
- }
- }
-}
-
-
-void LLGLSLShader::uniform1fv(U32 index, U32 count, const GLfloat* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- const auto& 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)
- {
- glUniform1fv(mUniform[index], count, v);
- mValue[mUniform[index]] = vec;
- }
- }
- }
-}
-
-void LLGLSLShader::uniform2fv(U32 index, U32 count, const GLfloat* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- const auto& 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)
- {
- glUniform2fv(mUniform[index], count, v);
- mValue[mUniform[index]] = vec;
- }
- }
- }
-}
-
-void LLGLSLShader::uniform3fv(U32 index, U32 count, const GLfloat* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- const auto& 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)
- {
- glUniform3fv(mUniform[index], count, v);
- mValue[mUniform[index]] = vec;
- }
- }
- }
-}
-
-void LLGLSLShader::uniform4fv(U32 index, U32 count, const GLfloat* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- const auto& 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)
- {
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- glUniform4fv(mUniform[index], count, v);
- mValue[mUniform[index]] = vec;
- }
- }
- }
-}
-
-void LLGLSLShader::uniformMatrix2fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- glUniformMatrix2fv(mUniform[index], count, transpose, v);
- }
- }
-}
-
-void LLGLSLShader::uniformMatrix3fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- glUniformMatrix3fv(mUniform[index], count, transpose, v);
- }
- }
-}
-
-void LLGLSLShader::uniformMatrix3x4fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- glUniformMatrix3x4fv(mUniform[index], count, transpose, v);
- }
- }
-}
-
-void LLGLSLShader::uniformMatrix4fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- llassert(sCurBoundShaderPtr == this);
-
- if (mProgramObject)
- {
- if (mUniform.size() <= index)
- {
- LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
- return;
- }
-
- if (mUniform[index] >= 0)
- {
- glUniformMatrix4fv(mUniform[index], count, transpose, v);
- }
- }
-}
-
-GLint LLGLSLShader::getUniformLocation(const LLStaticHashedString& uniform)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- GLint ret = -1;
- if (mProgramObject)
- {
- LLStaticStringTable<GLint>::iterator iter = mUniformMap.find(uniform);
- if (iter != mUniformMap.end())
- {
- if (gDebugGL)
- {
- stop_glerror();
- if (iter->second != glGetUniformLocation(mProgramObject, uniform.String().c_str()))
- {
- LL_ERRS() << "Uniform does not match." << LL_ENDL;
- }
- stop_glerror();
- }
- ret = iter->second;
- }
- }
-
- return ret;
-}
-
-GLint LLGLSLShader::getUniformLocation(U32 index)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- GLint ret = -1;
- if (mProgramObject)
- {
- if (index >= mUniform.size())
- {
- LL_WARNS_ONCE("Shader") << "Uniform index " << index << " out of bounds " << (S32)mUniform.size() << LL_ENDL;
- return ret;
- }
- return mUniform[index];
- }
-
- return ret;
-}
-
-GLint LLGLSLShader::getAttribLocation(U32 attrib)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
- if (attrib < mAttribute.size())
- {
- return mAttribute[attrib];
- }
- else
- {
- return -1;
- }
-}
-
-void LLGLSLShader::uniform1i(const LLStaticHashedString& uniform, GLint v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- GLint location = getUniformLocation(uniform);
-
- if (location >= 0)
- {
- const auto& iter = mValue.find(location);
- LLVector4 vec(v, 0.f, 0.f, 0.f);
- if (iter == mValue.end() || shouldChange(iter->second, vec))
- {
- glUniform1i(location, v);
- mValue[location] = vec;
- }
- }
-}
-
-void LLGLSLShader::uniform1iv(const LLStaticHashedString& uniform, U32 count, const GLint* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- GLint location = getUniformLocation(uniform);
-
- if (location >= 0)
- {
- LLVector4 vec(v[0], 0, 0, 0);
- const auto& iter = mValue.find(location);
- if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1)
- {
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- glUniform1iv(location, count, v);
- mValue[location] = vec;
- }
- }
-}
-
-void LLGLSLShader::uniform4iv(const LLStaticHashedString& uniform, U32 count, const GLint* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- GLint location = getUniformLocation(uniform);
-
- if (location >= 0)
- {
- LLVector4 vec(v[0], v[1], v[2], v[3]);
- const auto& iter = mValue.find(location);
- if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1)
- {
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- glUniform4iv(location, count, v);
- mValue[location] = vec;
- }
- }
-}
-
-void LLGLSLShader::uniform2i(const LLStaticHashedString& uniform, GLint i, GLint j)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- GLint location = getUniformLocation(uniform);
-
- if (location >= 0)
- {
- const auto& iter = mValue.find(location);
- LLVector4 vec(i, j, 0.f, 0.f);
- if (iter == mValue.end() || shouldChange(iter->second, vec))
- {
- glUniform2i(location, i, j);
- mValue[location] = vec;
- }
- }
-}
-
-
-void LLGLSLShader::uniform1f(const LLStaticHashedString& uniform, GLfloat v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- GLint location = getUniformLocation(uniform);
-
- if (location >= 0)
- {
- const auto& iter = mValue.find(location);
- LLVector4 vec(v, 0.f, 0.f, 0.f);
- if (iter == mValue.end() || shouldChange(iter->second, vec))
- {
- glUniform1f(location, v);
- mValue[location] = vec;
- }
- }
-}
-
-void LLGLSLShader::uniform2f(const LLStaticHashedString& uniform, GLfloat x, GLfloat y)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- GLint location = getUniformLocation(uniform);
-
- if (location >= 0)
- {
- const auto& iter = mValue.find(location);
- LLVector4 vec(x, y, 0.f, 0.f);
- if (iter == mValue.end() || shouldChange(iter->second, vec))
- {
- glUniform2f(location, x, y);
- mValue[location] = vec;
- }
- }
-
-}
-
-void LLGLSLShader::uniform3f(const LLStaticHashedString& uniform, GLfloat x, GLfloat y, GLfloat z)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- GLint location = getUniformLocation(uniform);
-
- if (location >= 0)
- {
- const auto& iter = mValue.find(location);
- LLVector4 vec(x, y, z, 0.f);
- if (iter == mValue.end() || shouldChange(iter->second, vec))
- {
- glUniform3f(location, x, y, z);
- mValue[location] = vec;
- }
- }
-}
-
-void LLGLSLShader::uniform1fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- GLint location = getUniformLocation(uniform);
-
- if (location >= 0)
- {
- const auto& iter = mValue.find(location);
- LLVector4 vec(v[0], 0.f, 0.f, 0.f);
- if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1)
- {
- glUniform1fv(location, count, v);
- mValue[location] = vec;
- }
- }
-}
-
-void LLGLSLShader::uniform2fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- GLint location = getUniformLocation(uniform);
-
- if (location >= 0)
- {
- const auto& iter = mValue.find(location);
- LLVector4 vec(v[0], v[1], 0.f, 0.f);
- if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1)
- {
- glUniform2fv(location, count, v);
- mValue[location] = vec;
- }
- }
-}
-
-void LLGLSLShader::uniform3fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- GLint location = getUniformLocation(uniform);
-
- if (location >= 0)
- {
- const auto& iter = mValue.find(location);
- LLVector4 vec(v[0], v[1], v[2], 0.f);
- if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1)
- {
- glUniform3fv(location, count, v);
- mValue[location] = vec;
- }
- }
-}
-
-void LLGLSLShader::uniform4fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- GLint location = getUniformLocation(uniform);
-
- if (location >= 0)
- {
- LLVector4 vec(v);
- const auto& iter = mValue.find(location);
- if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1)
- {
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- glUniform4fv(location, count, v);
- mValue[location] = vec;
- }
- }
-}
-
-void LLGLSLShader::uniformMatrix4fv(const LLStaticHashedString& uniform, U32 count, GLboolean transpose, const GLfloat* v)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- GLint location = getUniformLocation(uniform);
-
- if (location >= 0)
- {
- stop_glerror();
- glUniformMatrix4fv(location, count, transpose, v);
- stop_glerror();
- }
-}
-
-
-void LLGLSLShader::vertexAttrib4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
-{
- if (mAttribute[index] > 0)
- {
- glVertexAttrib4f(mAttribute[index], x, y, z, w);
- }
-}
-
-void LLGLSLShader::vertexAttrib4fv(U32 index, GLfloat* v)
-{
- if (mAttribute[index] > 0)
- {
- glVertexAttrib4fv(mAttribute[index], v);
- }
-}
-
-void LLGLSLShader::setMinimumAlpha(F32 minimum)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- gGL.flush();
- uniform1f(LLShaderMgr::MINIMUM_ALPHA, minimum);
-}
-
-void LLShaderUniforms::apply(LLGLSLShader* shader)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- for (auto& uniform : mIntegers)
- {
- shader->uniform1i(uniform.mUniform, uniform.mValue);
- }
-
- for (auto& uniform : mFloats)
- {
- shader->uniform1f(uniform.mUniform, uniform.mValue);
- }
-
- for (auto& uniform : mVectors)
- {
- shader->uniform4fv(uniform.mUniform, 1, uniform.mValue.mV);
- }
-
- for (auto& uniform : mVector3s)
- {
- shader->uniform3fv(uniform.mUniform, 1, uniform.mValue.mV);
- }
-}
-
-LLUUID LLGLSLShader::hash()
-{
- HBXXH128 hash_obj;
- hash_obj.update(mName);
- hash_obj.update(&mShaderGroup, sizeof(mShaderGroup));
- hash_obj.update(&mShaderLevel, sizeof(mShaderLevel));
- for (const auto& shdr_pair : mShaderFiles)
- {
- hash_obj.update(shdr_pair.first);
- hash_obj.update(&shdr_pair.second, sizeof(GLenum));
- }
- for (const auto& define_pair : mDefines)
- {
- hash_obj.update(define_pair.first);
- hash_obj.update(define_pair.second);
-
- }
- for (const auto& define_pair : LLGLSLShader::sGlobalDefines)
- {
- hash_obj.update(define_pair.first);
- hash_obj.update(define_pair.second);
-
- }
- hash_obj.update(&mFeatures, sizeof(LLShaderFeatures));
- hash_obj.update(gGLManager.mGLVendor);
- hash_obj.update(gGLManager.mGLRenderer);
- hash_obj.update(gGLManager.mGLVersionString);
- return hash_obj.digest();
-}
-
-#ifdef LL_PROFILER_ENABLE_RENDER_DOC
-void LLGLSLShader::setLabel(const char* label) {
- LL_LABEL_OBJECT_GL(GL_PROGRAM, mProgramObject, strlen(label), label);
-}
-#endif
+/**
+ * @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"
+#include "llvertexbuffer.h"
+#include "llrendertarget.h"
+
+#include "hbxxh.h"
+#include "llsdserialize.h"
+
+#if LL_DARWIN
+#include "OpenGL/OpenGL.h"
+#endif
+
+ // Print-print list of shader included source files that are linked together via glAttachShader()
+ // i.e. On macOS / OSX the AMD GLSL linker will display an error if a varying is left in an undefined state.
+#define DEBUG_SHADER_INCLUDES 0
+
+// 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;
+
+GLuint LLGLSLShader::sCurBoundShader = 0;
+LLGLSLShader* LLGLSLShader::sCurBoundShaderPtr = NULL;
+S32 LLGLSLShader::sIndexedTextureChannels = 0;
+bool LLGLSLShader::sProfileEnabled = false;
+std::set<LLGLSLShader*> LLGLSLShader::sInstances;
+LLGLSLShader::defines_map_t LLGLSLShader::sGlobalDefines;
+U64 LLGLSLShader::sTotalTimeElapsed = 0;
+U32 LLGLSLShader::sTotalTrianglesDrawn = 0;
+U64 LLGLSLShader::sTotalSamplesDrawn = 0;
+U32 LLGLSLShader::sTotalBinds = 0;
+
+//UI shader -- declared here so llui_libtest will link properly
+LLGLSLShader gUIProgram;
+LLGLSLShader gSolidColorProgram;
+
+// NOTE: Keep gShaderConsts* and LLGLSLShader::ShaderConsts_e in sync!
+const std::string gShaderConstsKey[LLGLSLShader::NUM_SHADER_CONSTS] =
+{
+ "LL_SHADER_CONST_CLOUD_MOON_DEPTH"
+ , "LL_SHADER_CONST_STAR_DEPTH"
+};
+
+// NOTE: Keep gShaderConsts* and LLGLSLShader::ShaderConsts_e in sync!
+const std::string gShaderConstsVal[LLGLSLShader::NUM_SHADER_CONSTS] =
+{
+ "0.99998" // SHADER_CONST_CLOUD_MOON_DEPTH // SL-14113
+ , "0.99999" // SHADER_CONST_STAR_DEPTH // SL-14113
+};
+
+
+bool shouldChange(const LLVector4& v1, const LLVector4& v2)
+{
+ return v1 != v2;
+}
+
+//===============================
+// LLGLSL Shader implementation
+//===============================
+
+//static
+void LLGLSLShader::initProfile()
+{
+ sProfileEnabled = true;
+ sTotalTimeElapsed = 0;
+ sTotalTrianglesDrawn = 0;
+ sTotalSamplesDrawn = 0;
+ sTotalBinds = 0;
+
+ for (std::set<LLGLSLShader*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
+ {
+ (*iter)->clearStats();
+ }
+}
+
+
+struct LLGLSLShaderCompareTimeElapsed
+{
+ bool operator()(const LLGLSLShader* const& lhs, const LLGLSLShader* const& rhs)
+ {
+ return lhs->mTimeElapsed < rhs->mTimeElapsed;
+ }
+};
+
+//static
+void LLGLSLShader::finishProfile(bool emit_report)
+{
+ sProfileEnabled = false;
+
+ if (emit_report)
+ {
+ std::vector<LLGLSLShader*> sorted;
+
+ for (std::set<LLGLSLShader*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
+ {
+ sorted.push_back(*iter);
+ }
+
+ std::sort(sorted.begin(), sorted.end(), LLGLSLShaderCompareTimeElapsed());
+
+ bool unbound = false;
+ for (std::vector<LLGLSLShader*>::iterator iter = sorted.begin(); iter != sorted.end(); ++iter)
+ {
+ (*iter)->dumpStats();
+ if ((*iter)->mBinds == 0)
+ {
+ unbound = true;
+ }
+ }
+
+ LL_INFOS() << "-----------------------------------" << LL_ENDL;
+ LL_INFOS() << "Total rendering time: " << llformat("%.4f ms", sTotalTimeElapsed / 1000000.f) << LL_ENDL;
+ LL_INFOS() << "Total samples drawn: " << llformat("%.4f million", sTotalSamplesDrawn / 1000000.f) << LL_ENDL;
+ LL_INFOS() << "Total triangles drawn: " << llformat("%.3f million", sTotalTrianglesDrawn / 1000000.f) << LL_ENDL;
+ LL_INFOS() << "-----------------------------------" << LL_ENDL;
+
+ if (unbound)
+ {
+ LL_INFOS() << "The following shaders were unused: " << LL_ENDL;
+ for (std::vector<LLGLSLShader*>::iterator iter = sorted.begin(); iter != sorted.end(); ++iter)
+ {
+ if ((*iter)->mBinds == 0)
+ {
+ LL_INFOS() << (*iter)->mName << LL_ENDL;
+ }
+ }
+ }
+ }
+}
+
+void LLGLSLShader::clearStats()
+{
+ mTrianglesDrawn = 0;
+ mTimeElapsed = 0;
+ mSamplesDrawn = 0;
+ mBinds = 0;
+}
+
+void LLGLSLShader::dumpStats()
+{
+ if (mBinds > 0)
+ {
+ LL_INFOS() << "=============================================" << LL_ENDL;
+ LL_INFOS() << mName << LL_ENDL;
+ for (U32 i = 0; i < mShaderFiles.size(); ++i)
+ {
+ LL_INFOS() << mShaderFiles[i].first << LL_ENDL;
+ }
+ LL_INFOS() << "=============================================" << LL_ENDL;
+
+ F32 ms = mTimeElapsed / 1000000.f;
+ F32 seconds = ms / 1000.f;
+
+ F32 pct_tris = (F32)mTrianglesDrawn / (F32)sTotalTrianglesDrawn * 100.f;
+ F32 tris_sec = (F32)(mTrianglesDrawn / 1000000.0);
+ tris_sec /= seconds;
+
+ F32 pct_samples = (F32)((F64)mSamplesDrawn / (F64)sTotalSamplesDrawn) * 100.f;
+ F32 samples_sec = (F32)mSamplesDrawn / 1000000000.0;
+ samples_sec /= seconds;
+
+ F32 pct_binds = (F32)mBinds / (F32)sTotalBinds * 100.f;
+
+ LL_INFOS() << "Triangles Drawn: " << mTrianglesDrawn << " " << llformat("(%.2f pct of total, %.3f million/sec)", pct_tris, tris_sec) << LL_ENDL;
+ LL_INFOS() << "Binds: " << mBinds << " " << llformat("(%.2f pct of total)", pct_binds) << LL_ENDL;
+ LL_INFOS() << "SamplesDrawn: " << mSamplesDrawn << " " << llformat("(%.2f pct of total, %.3f billion/sec)", pct_samples, samples_sec) << LL_ENDL;
+ LL_INFOS() << "Time Elapsed: " << mTimeElapsed << " " << llformat("(%.2f pct of total, %.5f ms)\n", (F32)((F64)mTimeElapsed / (F64)sTotalTimeElapsed) * 100.f, ms) << LL_ENDL;
+ }
+}
+
+//static
+void LLGLSLShader::startProfile()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ if (sProfileEnabled && sCurBoundShaderPtr)
+ {
+ sCurBoundShaderPtr->placeProfileQuery();
+ }
+
+}
+
+//static
+void LLGLSLShader::stopProfile()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ if (sProfileEnabled && sCurBoundShaderPtr)
+ {
+ sCurBoundShaderPtr->unbind();
+ }
+}
+
+void LLGLSLShader::placeProfileQuery(bool for_runtime)
+{
+ if (sProfileEnabled || for_runtime)
+ {
+ if (mTimerQuery == 0)
+ {
+ glGenQueries(1, &mSamplesQuery);
+ glGenQueries(1, &mTimerQuery);
+ glGenQueries(1, &mPrimitivesQuery);
+ }
+
+ glBeginQuery(GL_TIME_ELAPSED, mTimerQuery);
+
+ if (!for_runtime)
+ {
+ glBeginQuery(GL_SAMPLES_PASSED, mSamplesQuery);
+ glBeginQuery(GL_PRIMITIVES_GENERATED, mPrimitivesQuery);
+ }
+ }
+}
+
+bool LLGLSLShader::readProfileQuery(bool for_runtime, bool force_read)
+{
+ if (sProfileEnabled || for_runtime)
+ {
+ if (!mProfilePending)
+ {
+ glEndQuery(GL_TIME_ELAPSED);
+ if (!for_runtime)
+ {
+ glEndQuery(GL_SAMPLES_PASSED);
+ glEndQuery(GL_PRIMITIVES_GENERATED);
+ }
+ mProfilePending = for_runtime;
+ }
+
+ if (mProfilePending && for_runtime && !force_read)
+ {
+ GLuint64 result = 0;
+ glGetQueryObjectui64v(mTimerQuery, GL_QUERY_RESULT_AVAILABLE, &result);
+
+ if (result != GL_TRUE)
+ {
+ return false;
+ }
+ }
+
+ GLuint64 time_elapsed = 0;
+ glGetQueryObjectui64v(mTimerQuery, GL_QUERY_RESULT, &time_elapsed);
+ mTimeElapsed += time_elapsed;
+ mProfilePending = false;
+
+ if (!for_runtime)
+ {
+ GLuint64 samples_passed = 0;
+ glGetQueryObjectui64v(mSamplesQuery, GL_QUERY_RESULT, &samples_passed);
+
+ U64 primitives_generated = 0;
+ glGetQueryObjectui64v(mPrimitivesQuery, GL_QUERY_RESULT, &primitives_generated);
+ sTotalTimeElapsed += time_elapsed;
+
+ sTotalSamplesDrawn += samples_passed;
+ mSamplesDrawn += samples_passed;
+
+ U32 tri_count = (U32)primitives_generated / 3;
+
+ mTrianglesDrawn += tri_count;
+ sTotalTrianglesDrawn += tri_count;
+
+ sTotalBinds++;
+ mBinds++;
+ }
+ }
+
+ return true;
+}
+
+
+
+LLGLSLShader::LLGLSLShader()
+ : mProgramObject(0),
+ mAttributeMask(0),
+ mTotalUniformSize(0),
+ mActiveTextureChannels(0),
+ mShaderLevel(0),
+ mShaderGroup(SG_DEFAULT),
+ mFeatures(),
+ mUniformsDirty(false),
+ mTimerQuery(0),
+ mSamplesQuery(0),
+ mPrimitivesQuery(0)
+{
+
+}
+
+LLGLSLShader::~LLGLSLShader()
+{
+}
+
+void LLGLSLShader::unload()
+{
+ mShaderFiles.clear();
+ mDefines.clear();
+ mFeatures = LLShaderFeatures();
+
+ unloadInternal();
+}
+
+void LLGLSLShader::unloadInternal()
+{
+ sInstances.erase(this);
+
+ stop_glerror();
+ mAttribute.clear();
+ mTexture.clear();
+ mUniform.clear();
+
+ if (mProgramObject)
+ {
+ GLuint obj[1024];
+ GLsizei count = 0;
+ glGetAttachedShaders(mProgramObject, 1024, &count, obj);
+
+ for (GLsizei i = 0; i < count; i++)
+ {
+ glDetachShader(mProgramObject, obj[i]);
+ }
+
+ for (GLsizei i = 0; i < count; i++)
+ {
+ if (glIsShader(obj[i]))
+ {
+ glDeleteShader(obj[i]);
+ }
+ }
+
+ glDeleteProgram(mProgramObject);
+
+ mProgramObject = 0;
+ }
+
+ if (mTimerQuery)
+ {
+ glDeleteQueries(1, &mTimerQuery);
+ mTimerQuery = 0;
+ }
+
+ if (mSamplesQuery)
+ {
+ glDeleteQueries(1, &mSamplesQuery);
+ mSamplesQuery = 0;
+ }
+
+ //hack to make apple not complain
+ glGetError();
+
+ stop_glerror();
+}
+
+bool LLGLSLShader::createShader(std::vector<LLStaticHashedString>* attributes,
+ std::vector<LLStaticHashedString>* uniforms,
+ U32 varying_count,
+ const char** varyings)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ unloadInternal();
+
+ sInstances.insert(this);
+
+ //reloading, reset matrix hash values
+ for (U32 i = 0; i < LLRender::NUM_MATRIX_MODES; ++i)
+ {
+ mMatHash[i] = 0xFFFFFFFF;
+ }
+ mLightHash = 0xFFFFFFFF;
+
+ llassert_always(!mShaderFiles.empty());
+
+#if LL_DARWIN
+ // work-around missing mix(vec3,vec3,bvec3)
+ mDefines["OLD_SELECT"] = "1";
+#endif
+
+ mShaderHash = hash();
+
+ // Create program
+ mProgramObject = glCreateProgram();
+ if (mProgramObject == 0)
+ {
+ // Shouldn't happen if shader related extensions, like ARB_vertex_shader, exist.
+ LL_SHADER_LOADING_WARNS() << "Failed to create handle for shader: " << mName << LL_ENDL;
+ unloadInternal();
+ return false;
+ }
+
+ bool success = true;
+
+ mUsingBinaryProgram = LLShaderMgr::instance()->loadCachedProgramBinary(this);
+
+ if (!mUsingBinaryProgram)
+ {
+#if DEBUG_SHADER_INCLUDES
+ fprintf(stderr, "--- %s ---\n", mName.c_str());
+#endif // DEBUG_SHADER_INCLUDES
+
+ //compile new source
+ vector< pair<string, GLenum> >::iterator fileIter = mShaderFiles.begin();
+ for (; fileIter != mShaderFiles.end(); fileIter++)
+ {
+ GLuint shaderhandle = LLShaderMgr::instance()->loadShaderFile((*fileIter).first, mShaderLevel, (*fileIter).second, &mDefines, mFeatures.mIndexedTextureChannels);
+ LL_DEBUGS("ShaderLoading") << "SHADER FILE: " << (*fileIter).first << " mShaderLevel=" << mShaderLevel << LL_ENDL;
+ if (shaderhandle)
+ {
+ attachObject(shaderhandle);
+ }
+ else
+ {
+ success = false;
+ }
+ }
+ }
+
+ // Attach existing objects
+ if (!LLShaderMgr::instance()->attachShaderFeatures(this))
+ {
+ unloadInternal();
+ return false;
+ }
+ // Map attributes and uniforms
+ if (success)
+ {
+ success = mapAttributes(attributes);
+ }
+ if (success)
+ {
+ success = mapUniforms(uniforms);
+ }
+ if (!success)
+ {
+ LL_SHADER_LOADING_WARNS() << "Failed to link shader: " << mName << LL_ENDL;
+
+ // Try again using a lower shader level;
+ if (mShaderLevel > 0)
+ {
+ LL_SHADER_LOADING_WARNS() << "Failed to link using shader level " << mShaderLevel << " trying again using shader level " << (mShaderLevel - 1) << LL_ENDL;
+ mShaderLevel--;
+ return createShader(attributes, uniforms);
+ }
+ else
+ {
+ // Give up and unload shader.
+ unloadInternal();
+ }
+ }
+ else if (mFeatures.mIndexedTextureChannels > 0)
+ { //override texture channels for indexed texture rendering
+ bind();
+ S32 channel_count = mFeatures.mIndexedTextureChannels;
+
+ for (S32 i = 0; i < channel_count; i++)
+ {
+ LLStaticHashedString uniName(llformat("tex%d", i));
+ uniform1i(uniName, i);
+ }
+
+ S32 cur_tex = channel_count; //adjust any texture channels that might have been overwritten
+ for (U32 i = 0; i < mTexture.size(); i++)
+ {
+ if (mTexture[i] > -1 && mTexture[i] < channel_count)
+ {
+ llassert(cur_tex < gGLManager.mNumTextureImageUnits);
+ uniform1i(i, cur_tex);
+ mTexture[i] = cur_tex++;
+ }
+ }
+ unbind();
+ }
+
+#ifdef LL_PROFILER_ENABLE_RENDER_DOC
+ setLabel(mName.c_str());
+#endif
+
+ return success;
+}
+
+#if DEBUG_SHADER_INCLUDES
+void dumpAttachObject(const char* func_name, GLuint program_object, const std::string& object_path)
+{
+ GLchar* info_log;
+ GLint info_len_expect = 0;
+ GLint info_len_actual = 0;
+
+ glGetShaderiv(program_object, GL_INFO_LOG_LENGTH, , &info_len_expect);
+ fprintf(stderr, " * %-20s(), log size: %d, %s\n", func_name, info_len_expect, object_path.c_str());
+
+ if (info_len_expect > 0)
+ {
+ fprintf(stderr, " ========== %s() ========== \n", func_name);
+ info_log = new GLchar[info_len_expect];
+ glGetProgramInfoLog(program_object, info_len_expect, &info_len_actual, info_log);
+ fprintf(stderr, "%s\n", info_log);
+ delete[] info_log;
+ }
+}
+#endif // DEBUG_SHADER_INCLUDES
+
+bool LLGLSLShader::attachVertexObject(std::string object_path)
+{
+ if (LLShaderMgr::instance()->mVertexShaderObjects.count(object_path) > 0)
+ {
+ stop_glerror();
+ glAttachShader(mProgramObject, LLShaderMgr::instance()->mVertexShaderObjects[object_path]);
+#if DEBUG_SHADER_INCLUDES
+ dumpAttachObject("attachVertexObject", mProgramObject, object_path);
+#endif // DEBUG_SHADER_INCLUDES
+ stop_glerror();
+ return true;
+ }
+ else
+ {
+ LL_SHADER_LOADING_WARNS() << "Attempting to attach shader object: '" << object_path << "' that hasn't been compiled." << LL_ENDL;
+ return false;
+ }
+}
+
+bool LLGLSLShader::attachFragmentObject(std::string object_path)
+{
+ if(mUsingBinaryProgram)
+ return true;
+
+ if (LLShaderMgr::instance()->mFragmentShaderObjects.count(object_path) > 0)
+ {
+ stop_glerror();
+ glAttachShader(mProgramObject, LLShaderMgr::instance()->mFragmentShaderObjects[object_path]);
+#if DEBUG_SHADER_INCLUDES
+ dumpAttachObject("attachFragmentObject", mProgramObject, object_path);
+#endif // DEBUG_SHADER_INCLUDES
+ stop_glerror();
+ return true;
+ }
+ else
+ {
+ LL_SHADER_LOADING_WARNS() << "Attempting to attach shader object: '" << object_path << "' that hasn't been compiled." << LL_ENDL;
+ return false;
+ }
+}
+
+void LLGLSLShader::attachObject(GLuint object)
+{
+ if(mUsingBinaryProgram)
+ return;
+
+ if (object != 0)
+ {
+ stop_glerror();
+ glAttachShader(mProgramObject, object);
+#if DEBUG_SHADER_INCLUDES
+ std::string object_path("???");
+ dumpAttachObject("attachObject", mProgramObject, object_path);
+#endif // DEBUG_SHADER_INCLUDES
+ stop_glerror();
+ }
+ else
+ {
+ LL_SHADER_LOADING_WARNS() << "Attempting to attach non existing shader object. " << LL_ENDL;
+ }
+}
+
+void LLGLSLShader::attachObjects(GLuint* objects, S32 count)
+{
+ if(mUsingBinaryProgram)
+ return;
+
+ for (S32 i = 0; i < count; i++)
+ {
+ attachObject(objects[i]);
+ }
+}
+
+bool LLGLSLShader::mapAttributes(const std::vector<LLStaticHashedString>* attributes)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ bool res = true;
+ if (!mUsingBinaryProgram)
+ {
+ //before linking, make sure reserved attributes always have consistent locations
+ for (U32 i = 0; i < LLShaderMgr::instance()->mReservedAttribs.size(); i++)
+ {
+ const char* name = LLShaderMgr::instance()->mReservedAttribs[i].c_str();
+ glBindAttribLocation(mProgramObject, i, (const GLchar*)name);
+ }
+
+ //link the program
+ res = link();
+ }
+
+ mAttribute.clear();
+ U32 numAttributes = (attributes == NULL) ? 0 : attributes->size();
+#if LL_RELEASE_WITH_DEBUG_INFO
+ mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, { -1, NULL });
+#else
+ mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, -1);
+#endif
+
+ if (res)
+ { //read back channel locations
+
+ mAttributeMask = 0;
+
+ //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 = glGetAttribLocation(mProgramObject, (const GLchar*)name);
+ if (index != -1)
+ {
+#if LL_RELEASE_WITH_DEBUG_INFO
+ mAttribute[i] = { index, name };
+#else
+ mAttribute[i] = index;
+#endif
+ mAttributeMask |= 1 << i;
+ LL_DEBUGS("ShaderUniform") << "Attribute " << name << " assigned to channel " << index << LL_ENDL;
+ }
+ }
+ if (attributes != NULL)
+ {
+ for (U32 i = 0; i < numAttributes; i++)
+ {
+ const char* name = (*attributes)[i].String().c_str();
+ S32 index = glGetAttribLocation(mProgramObject, name);
+ if (index != -1)
+ {
+ mAttribute[LLShaderMgr::instance()->mReservedAttribs.size() + i] = index;
+ LL_DEBUGS("ShaderUniform") << "Attribute " << name << " assigned to channel " << index << LL_ENDL;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void LLGLSLShader::mapUniform(GLint index, const vector<LLStaticHashedString>* uniforms)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ if (index == -1)
+ {
+ return;
+ }
+
+ GLenum type;
+ GLsizei length;
+ GLint size = -1;
+ char name[1024]; /* Flawfinder: ignore */
+ name[0] = 0;
+
+
+ glGetActiveUniform(mProgramObject, index, 1024, &length, &size, &type, (GLchar*)name);
+ if (size > 0)
+ {
+ switch (type)
+ {
+ case GL_FLOAT_VEC2: size *= 2; break;
+ case GL_FLOAT_VEC3: size *= 3; break;
+ case GL_FLOAT_VEC4: size *= 4; break;
+ case GL_DOUBLE: size *= 2; break;
+ case GL_DOUBLE_VEC2: size *= 2; break;
+ case GL_DOUBLE_VEC3: size *= 6; break;
+ case GL_DOUBLE_VEC4: size *= 8; break;
+ case GL_INT_VEC2: size *= 2; break;
+ case GL_INT_VEC3: size *= 3; break;
+ case GL_INT_VEC4: size *= 4; break;
+ case GL_UNSIGNED_INT_VEC2: size *= 2; break;
+ case GL_UNSIGNED_INT_VEC3: size *= 3; break;
+ case GL_UNSIGNED_INT_VEC4: size *= 4; break;
+ case GL_BOOL_VEC2: size *= 2; break;
+ case GL_BOOL_VEC3: size *= 3; break;
+ case GL_BOOL_VEC4: size *= 4; break;
+ case GL_FLOAT_MAT2: size *= 4; break;
+ case GL_FLOAT_MAT3: size *= 9; break;
+ case GL_FLOAT_MAT4: size *= 16; break;
+ case GL_FLOAT_MAT2x3: size *= 6; break;
+ case GL_FLOAT_MAT2x4: size *= 8; break;
+ case GL_FLOAT_MAT3x2: size *= 6; break;
+ case GL_FLOAT_MAT3x4: size *= 12; break;
+ case GL_FLOAT_MAT4x2: size *= 8; break;
+ case GL_FLOAT_MAT4x3: size *= 12; break;
+ case GL_DOUBLE_MAT2: size *= 8; break;
+ case GL_DOUBLE_MAT3: size *= 18; break;
+ case GL_DOUBLE_MAT4: size *= 32; break;
+ case GL_DOUBLE_MAT2x3: size *= 12; break;
+ case GL_DOUBLE_MAT2x4: size *= 16; break;
+ case GL_DOUBLE_MAT3x2: size *= 12; break;
+ case GL_DOUBLE_MAT3x4: size *= 24; break;
+ case GL_DOUBLE_MAT4x2: size *= 16; break;
+ case GL_DOUBLE_MAT4x3: size *= 24; break;
+ }
+ mTotalUniformSize += size;
+ }
+
+ S32 location = glGetUniformLocation(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;
+ }
+
+ LLStaticHashedString hashedName(name);
+ mUniformMap[hashedName] = location;
+
+ LL_DEBUGS("ShaderUniform") << "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] == name))
+ {
+ //found it
+ mUniform[i] = location;
+ mTexture[i] = mapUniformTextureChannel(location, type, size);
+ return;
+ }
+ }
+
+ if (uniforms != NULL)
+ {
+ for (U32 i = 0; i < uniforms->size(); i++)
+ {
+ if ((mUniform[i + LLShaderMgr::instance()->mReservedUniforms.size()] == -1)
+ && ((*uniforms)[i].String() == name))
+ {
+ //found it
+ mUniform[i + LLShaderMgr::instance()->mReservedUniforms.size()] = location;
+ mTexture[i + LLShaderMgr::instance()->mReservedUniforms.size()] = mapUniformTextureChannel(location, type, size);
+ return;
+ }
+ }
+ }
+ }
+}
+
+void LLGLSLShader::clearPermutations()
+{
+ mDefines.clear();
+}
+
+void LLGLSLShader::addPermutation(std::string name, std::string value)
+{
+ mDefines[name] = value;
+}
+
+void LLGLSLShader::addConstant(const LLGLSLShader::eShaderConsts shader_const)
+{
+ addPermutation(gShaderConstsKey[shader_const], gShaderConstsVal[shader_const]);
+}
+
+void LLGLSLShader::removePermutation(std::string name)
+{
+ mDefines.erase(name);
+}
+
+GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type, GLint size)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ if ((type >= GL_SAMPLER_1D && type <= GL_SAMPLER_2D_RECT_SHADOW) ||
+ type == GL_SAMPLER_2D_MULTISAMPLE ||
+ type == GL_SAMPLER_CUBE_MAP_ARRAY)
+ { //this here is a texture
+ GLint ret = mActiveTextureChannels;
+ if (size == 1)
+ {
+ glUniform1i(location, mActiveTextureChannels);
+ LL_DEBUGS("ShaderUniform") << "Assigned to texture channel " << mActiveTextureChannels << LL_ENDL;
+ mActiveTextureChannels++;
+ }
+ else
+ {
+ //is array of textures, make sequential after this texture
+ GLint channel[32]; // <=== only support up to 32 texture channels
+ llassert(size <= 32);
+ size = llmin(size, 32);
+ for (int i = 0; i < size; ++i)
+ {
+ channel[i] = mActiveTextureChannels++;
+ }
+ glUniform1iv(location, size, channel);
+ LL_DEBUGS("ShaderUniform") << "Assigned to texture channel " <<
+ (mActiveTextureChannels - size) << " through " << (mActiveTextureChannels - 1) << LL_ENDL;
+ }
+
+ llassert(mActiveTextureChannels <= 32); // too many textures (probably)
+ return ret;
+ }
+ return -1;
+}
+
+bool LLGLSLShader::mapUniforms(const vector<LLStaticHashedString>* uniforms)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ bool res = true;
+
+ mTotalUniformSize = 0;
+ 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;
+ glGetProgramiv(mProgramObject, GL_ACTIVE_UNIFORMS, &activeCount);
+
+ //........................................................................................................................................
+ //........................................................................................
+
+ /*
+ EXPLANATION:
+ This is part of code is temporary because as the final result the mapUniform() should be rewrited.
+ But it's a huge a volume of work which is need to be a more carefully performed for avoid possible
+ regression's (i.e. it should be formalized a separate ticket in JIRA).
+
+ RESON:
+ The reason of this code is that SL engine is very sensitive to fact that "diffuseMap" should be appear
+ first as uniform parameter which is should get 0-"texture channel" index (see mapUniformTextureChannel() and mActiveTextureChannels)
+ it influence to which is texture matrix will be updated during rendering.
+
+ But, order of indexe's of uniform variables is not defined and GLSL compiler can change it as want
+ , even if the "diffuseMap" will be appear and use first in shader code.
+
+ As example where this situation appear see: "Deferred Material Shader 28/29/30/31"
+ And tickets: MAINT-4165, MAINT-4839, MAINT-3568, MAINT-6437
+
+ --- davep TODO -- pretty sure the entire block here is superstitious and that the uniform index has nothing to do with the texture channel
+ texture channel should follow the uniform VALUE
+ */
+
+
+ S32 diffuseMap = glGetUniformLocation(mProgramObject, "diffuseMap");
+ S32 specularMap = glGetUniformLocation(mProgramObject, "specularMap");
+ S32 bumpMap = glGetUniformLocation(mProgramObject, "bumpMap");
+ S32 altDiffuseMap = glGetUniformLocation(mProgramObject, "altDiffuseMap");
+ S32 environmentMap = glGetUniformLocation(mProgramObject, "environmentMap");
+ S32 reflectionMap = glGetUniformLocation(mProgramObject, "reflectionMap");
+
+ std::set<S32> skip_index;
+
+ if (-1 != diffuseMap && (-1 != specularMap || -1 != bumpMap || -1 != environmentMap || -1 != altDiffuseMap))
+ {
+ GLenum type;
+ GLsizei length;
+ GLint size = -1;
+ char name[1024];
+
+ diffuseMap = altDiffuseMap = specularMap = bumpMap = environmentMap = -1;
+
+ for (S32 i = 0; i < activeCount; i++)
+ {
+ name[0] = '\0';
+
+ glGetActiveUniform(mProgramObject, i, 1024, &length, &size, &type, (GLchar*)name);
+
+ if (-1 == diffuseMap && std::string(name) == "diffuseMap")
+ {
+ diffuseMap = i;
+ continue;
+ }
+
+ if (-1 == specularMap && std::string(name) == "specularMap")
+ {
+ specularMap = i;
+ continue;
+ }
+
+ if (-1 == bumpMap && std::string(name) == "bumpMap")
+ {
+ bumpMap = i;
+ continue;
+ }
+
+ if (-1 == environmentMap && std::string(name) == "environmentMap")
+ {
+ environmentMap = i;
+ continue;
+ }
+
+ if (-1 == reflectionMap && std::string(name) == "reflectionMap")
+ {
+ reflectionMap = i;
+ continue;
+ }
+
+ if (-1 == altDiffuseMap && std::string(name) == "altDiffuseMap")
+ {
+ altDiffuseMap = i;
+ continue;
+ }
+ }
+
+ bool specularDiff = specularMap < diffuseMap && -1 != specularMap;
+ bool bumpLessDiff = bumpMap < diffuseMap && -1 != bumpMap;
+ bool envLessDiff = environmentMap < diffuseMap && -1 != environmentMap;
+ bool refLessDiff = reflectionMap < diffuseMap && -1 != reflectionMap;
+
+ if (specularDiff || bumpLessDiff || envLessDiff || refLessDiff)
+ {
+ mapUniform(diffuseMap, uniforms);
+ skip_index.insert(diffuseMap);
+
+ if (-1 != specularMap) {
+ mapUniform(specularMap, uniforms);
+ skip_index.insert(specularMap);
+ }
+
+ if (-1 != bumpMap) {
+ mapUniform(bumpMap, uniforms);
+ skip_index.insert(bumpMap);
+ }
+
+ if (-1 != environmentMap) {
+ mapUniform(environmentMap, uniforms);
+ skip_index.insert(environmentMap);
+ }
+
+ if (-1 != reflectionMap) {
+ mapUniform(reflectionMap, uniforms);
+ skip_index.insert(reflectionMap);
+ }
+ }
+ }
+
+ //........................................................................................
+
+ for (S32 i = 0; i < activeCount; i++)
+ {
+ //........................................................................................
+ if (skip_index.end() != skip_index.find(i)) continue;
+ //........................................................................................
+
+ mapUniform(i, uniforms);
+ }
+ //........................................................................................................................................
+
+ if (mFeatures.hasReflectionProbes) // Set up block binding, in a way supported by Apple (rather than binding = 1 in .glsl).
+ { // See slide 35 and more of https://docs.huihoo.com/apple/wwdc/2011/session_420__advances_in_opengl_for_mac_os_x_lion.pdf
+ static const GLuint BLOCKBINDING = 1; //picked by us
+ //Get the index, similar to a uniform location
+ GLuint UBOBlockIndex = glGetUniformBlockIndex(mProgramObject, "ReflectionProbes");
+ if (UBOBlockIndex != GL_INVALID_INDEX)
+ {
+ //Set this index to a binding index
+ glUniformBlockBinding(mProgramObject, UBOBlockIndex, BLOCKBINDING);
+ }
+ }
+ unbind();
+
+ LL_DEBUGS("ShaderUniform") << "Total Uniform Size: " << mTotalUniformSize << LL_ENDL;
+ return res;
+}
+
+
+bool LLGLSLShader::link(bool suppress_errors)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ bool success = LLShaderMgr::instance()->linkProgramObject(mProgramObject, suppress_errors);
+
+ if (!success && !suppress_errors)
+ {
+ LLShaderMgr::instance()->dumpObjectLog(mProgramObject, !success, mName);
+ }
+
+ if (success)
+ {
+ LLShaderMgr::instance()->saveCachedProgramBinary(this);
+ }
+
+ return success;
+}
+
+void LLGLSLShader::bind()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ llassert(mProgramObject != 0);
+
+ gGL.flush();
+
+ if (sCurBoundShader != mProgramObject) // Don't re-bind current shader
+ {
+ if (sCurBoundShaderPtr)
+ {
+ sCurBoundShaderPtr->readProfileQuery();
+ }
+ LLVertexBuffer::unbind();
+ glUseProgram(mProgramObject);
+ sCurBoundShader = mProgramObject;
+ sCurBoundShaderPtr = this;
+ placeProfileQuery();
+ LLVertexBuffer::setupClientArrays(mAttributeMask);
+ }
+
+ if (mUniformsDirty)
+ {
+ LLShaderMgr::instance()->updateShaderUniforms(this);
+ mUniformsDirty = false;
+ }
+}
+
+void LLGLSLShader::bind(bool rigged)
+{
+ if (rigged)
+ {
+ llassert(mRiggedVariant);
+ mRiggedVariant->bind();
+ }
+ else
+ {
+ bind();
+ }
+}
+
+void LLGLSLShader::unbind(void)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ gGL.flush();
+ LLVertexBuffer::unbind();
+
+ if (sCurBoundShaderPtr)
+ {
+ sCurBoundShaderPtr->readProfileQuery();
+ }
+
+ glUseProgram(0);
+ sCurBoundShader = 0;
+ sCurBoundShaderPtr = NULL;
+}
+
+S32 LLGLSLShader::bindTexture(const std::string& uniform, LLTexture* texture, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace colorspace)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ S32 channel = 0;
+ channel = getUniformLocation(uniform);
+
+ return bindTexture(channel, texture, mode, colorspace);
+}
+
+S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace colorspace)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ if (uniform < 0 || uniform >= (S32)mTexture.size())
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL;
+ return -1;
+ }
+
+ uniform = mTexture[uniform];
+
+ if (uniform > -1)
+ {
+ gGL.getTexUnit(uniform)->bindFast(texture);
+ gGL.getTexUnit(uniform)->setTextureColorSpace(colorspace);
+ }
+
+ return uniform;
+}
+
+S32 LLGLSLShader::bindTexture(S32 uniform, LLRenderTarget* texture, bool depth, LLTexUnit::eTextureFilterOptions mode, U32 index)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ if (uniform < 0 || uniform >= (S32)mTexture.size())
+ {
+ return -1;
+ }
+
+ uniform = getTextureChannel(uniform);
+
+ if (uniform > -1)
+ {
+ if (depth) {
+ gGL.getTexUnit(uniform)->bind(texture, true);
+ }
+ else {
+ bool has_mips = mode == LLTexUnit::TFO_TRILINEAR || mode == LLTexUnit::TFO_ANISOTROPIC;
+ gGL.getTexUnit(uniform)->bindManual(texture->getUsage(), texture->getTexture(index), has_mips);
+ }
+
+ gGL.getTexUnit(uniform)->setTextureFilteringOption(mode);
+ }
+
+ return uniform;
+}
+
+S32 LLGLSLShader::bindTexture(const std::string& uniform, LLRenderTarget* texture, bool depth, LLTexUnit::eTextureFilterOptions mode)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ S32 channel = 0;
+ channel = getUniformLocation(uniform);
+
+ return bindTexture(channel, texture, depth, mode);
+}
+
+S32 LLGLSLShader::unbindTexture(const std::string& uniform, LLTexUnit::eTextureType mode)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ S32 channel = 0;
+ channel = getUniformLocation(uniform);
+
+ return unbindTexture(channel);
+}
+
+S32 LLGLSLShader::unbindTexture(S32 uniform, LLTexUnit::eTextureType mode)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ if (uniform < 0 || uniform >= (S32)mTexture.size())
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL;
+ return -1;
+ }
+
+ uniform = mTexture[uniform];
+
+ if (uniform > -1)
+ {
+ gGL.getTexUnit(uniform)->unbindFast(mode);
+ }
+
+ return uniform;
+}
+
+S32 LLGLSLShader::getTextureChannel(S32 uniform) const
+{
+ return mTexture[uniform];
+}
+
+S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace space)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ if (uniform < 0 || uniform >= (S32)mTexture.size())
+ {
+ LL_SHADER_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);
+ gGL.getTexUnit(index)->setTextureColorSpace(space);
+ }
+ return index;
+}
+
+S32 LLGLSLShader::disableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace space)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ if (uniform < 0 || uniform >= (S32)mTexture.size())
+ {
+ LL_SHADER_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 && gGL.getTexUnit(index)->getCurrColorSpace() != space)
+ {
+ if (gDebugSession)
+ {
+ gFailLog << "Texture channel " << index << " texture type corrupted." << std::endl;
+ ll_fail("LLGLSLShader::disableTexture failed");
+ }
+ else
+ {
+ LL_ERRS() << "Texture channel " << index << " texture type corrupted." << LL_ENDL;
+ }
+ }
+ gGL.getTexUnit(index)->disable();
+ }
+ return index;
+}
+
+void LLGLSLShader::uniform1i(U32 index, GLint x)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ const auto& iter = mValue.find(mUniform[index]);
+ if (iter == mValue.end() || iter->second.mV[0] != x)
+ {
+ glUniform1i(mUniform[index], x);
+ mValue[mUniform[index]] = LLVector4(x, 0.f, 0.f, 0.f);
+ }
+ }
+ }
+}
+
+void LLGLSLShader::uniform1f(U32 index, GLfloat x)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ const auto& iter = mValue.find(mUniform[index]);
+ if (iter == mValue.end() || iter->second.mV[0] != x)
+ {
+ glUniform1f(mUniform[index], x);
+ mValue[mUniform[index]] = LLVector4(x, 0.f, 0.f, 0.f);
+ }
+ }
+ }
+}
+
+void LLGLSLShader::fastUniform1f(U32 index, GLfloat x)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+ llassert(mProgramObject);
+ llassert(mUniform.size() <= index);
+ llassert(mUniform[index] >= 0);
+ glUniform1f(mUniform[index], x);
+}
+
+void LLGLSLShader::uniform2f(U32 index, GLfloat x, GLfloat y)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ const auto& iter = mValue.find(mUniform[index]);
+ LLVector4 vec(x, y, 0.f, 0.f);
+ if (iter == mValue.end() || shouldChange(iter->second, vec))
+ {
+ glUniform2f(mUniform[index], x, y);
+ mValue[mUniform[index]] = vec;
+ }
+ }
+ }
+}
+
+void LLGLSLShader::uniform3f(U32 index, GLfloat x, GLfloat y, GLfloat z)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ const auto& iter = mValue.find(mUniform[index]);
+ LLVector4 vec(x, y, z, 0.f);
+ if (iter == mValue.end() || shouldChange(iter->second, vec))
+ {
+ glUniform3f(mUniform[index], x, y, z);
+ mValue[mUniform[index]] = vec;
+ }
+ }
+ }
+}
+
+void LLGLSLShader::uniform4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ const auto& iter = mValue.find(mUniform[index]);
+ LLVector4 vec(x, y, z, w);
+ if (iter == mValue.end() || shouldChange(iter->second, vec))
+ {
+ glUniform4f(mUniform[index], x, y, z, w);
+ mValue[mUniform[index]] = vec;
+ }
+ }
+ }
+}
+
+void LLGLSLShader::uniform1iv(U32 index, U32 count, const GLint* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ const auto& 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)
+ {
+ glUniform1iv(mUniform[index], count, v);
+ mValue[mUniform[index]] = vec;
+ }
+ }
+ }
+}
+
+void LLGLSLShader::uniform4iv(U32 index, U32 count, const GLint* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ const auto& 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)
+ {
+ glUniform1iv(mUniform[index], count, v);
+ mValue[mUniform[index]] = vec;
+ }
+ }
+ }
+}
+
+
+void LLGLSLShader::uniform1fv(U32 index, U32 count, const GLfloat* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ const auto& 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)
+ {
+ glUniform1fv(mUniform[index], count, v);
+ mValue[mUniform[index]] = vec;
+ }
+ }
+ }
+}
+
+void LLGLSLShader::uniform2fv(U32 index, U32 count, const GLfloat* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ const auto& 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)
+ {
+ glUniform2fv(mUniform[index], count, v);
+ mValue[mUniform[index]] = vec;
+ }
+ }
+ }
+}
+
+void LLGLSLShader::uniform3fv(U32 index, U32 count, const GLfloat* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ const auto& 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)
+ {
+ glUniform3fv(mUniform[index], count, v);
+ mValue[mUniform[index]] = vec;
+ }
+ }
+ }
+}
+
+void LLGLSLShader::uniform4fv(U32 index, U32 count, const GLfloat* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ const auto& 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)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ glUniform4fv(mUniform[index], count, v);
+ mValue[mUniform[index]] = vec;
+ }
+ }
+ }
+}
+
+void LLGLSLShader::uniformMatrix2fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ glUniformMatrix2fv(mUniform[index], count, transpose, v);
+ }
+ }
+}
+
+void LLGLSLShader::uniformMatrix3fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ glUniformMatrix3fv(mUniform[index], count, transpose, v);
+ }
+ }
+}
+
+void LLGLSLShader::uniformMatrix3x4fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ glUniformMatrix3x4fv(mUniform[index], count, transpose, v);
+ }
+ }
+}
+
+void LLGLSLShader::uniformMatrix4fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ llassert(sCurBoundShaderPtr == this);
+
+ if (mProgramObject)
+ {
+ if (mUniform.size() <= index)
+ {
+ LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL;
+ return;
+ }
+
+ if (mUniform[index] >= 0)
+ {
+ glUniformMatrix4fv(mUniform[index], count, transpose, v);
+ }
+ }
+}
+
+GLint LLGLSLShader::getUniformLocation(const LLStaticHashedString& uniform)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ GLint ret = -1;
+ if (mProgramObject)
+ {
+ LLStaticStringTable<GLint>::iterator iter = mUniformMap.find(uniform);
+ if (iter != mUniformMap.end())
+ {
+ if (gDebugGL)
+ {
+ stop_glerror();
+ if (iter->second != glGetUniformLocation(mProgramObject, uniform.String().c_str()))
+ {
+ LL_ERRS() << "Uniform does not match." << LL_ENDL;
+ }
+ stop_glerror();
+ }
+ ret = iter->second;
+ }
+ }
+
+ return ret;
+}
+
+GLint LLGLSLShader::getUniformLocation(U32 index)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ GLint ret = -1;
+ if (mProgramObject)
+ {
+ if (index >= mUniform.size())
+ {
+ LL_WARNS_ONCE("Shader") << "Uniform index " << index << " out of bounds " << (S32)mUniform.size() << LL_ENDL;
+ return ret;
+ }
+ return mUniform[index];
+ }
+
+ return ret;
+}
+
+GLint LLGLSLShader::getAttribLocation(U32 attrib)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+
+ if (attrib < mAttribute.size())
+ {
+ return mAttribute[attrib];
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+void LLGLSLShader::uniform1i(const LLStaticHashedString& uniform, GLint v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ GLint location = getUniformLocation(uniform);
+
+ if (location >= 0)
+ {
+ const auto& iter = mValue.find(location);
+ LLVector4 vec(v, 0.f, 0.f, 0.f);
+ if (iter == mValue.end() || shouldChange(iter->second, vec))
+ {
+ glUniform1i(location, v);
+ mValue[location] = vec;
+ }
+ }
+}
+
+void LLGLSLShader::uniform1iv(const LLStaticHashedString& uniform, U32 count, const GLint* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ GLint location = getUniformLocation(uniform);
+
+ if (location >= 0)
+ {
+ LLVector4 vec(v[0], 0, 0, 0);
+ const auto& iter = mValue.find(location);
+ if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ glUniform1iv(location, count, v);
+ mValue[location] = vec;
+ }
+ }
+}
+
+void LLGLSLShader::uniform4iv(const LLStaticHashedString& uniform, U32 count, const GLint* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ GLint location = getUniformLocation(uniform);
+
+ if (location >= 0)
+ {
+ LLVector4 vec(v[0], v[1], v[2], v[3]);
+ const auto& iter = mValue.find(location);
+ if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ glUniform4iv(location, count, v);
+ mValue[location] = vec;
+ }
+ }
+}
+
+void LLGLSLShader::uniform2i(const LLStaticHashedString& uniform, GLint i, GLint j)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ GLint location = getUniformLocation(uniform);
+
+ if (location >= 0)
+ {
+ const auto& iter = mValue.find(location);
+ LLVector4 vec(i, j, 0.f, 0.f);
+ if (iter == mValue.end() || shouldChange(iter->second, vec))
+ {
+ glUniform2i(location, i, j);
+ mValue[location] = vec;
+ }
+ }
+}
+
+
+void LLGLSLShader::uniform1f(const LLStaticHashedString& uniform, GLfloat v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ GLint location = getUniformLocation(uniform);
+
+ if (location >= 0)
+ {
+ const auto& iter = mValue.find(location);
+ LLVector4 vec(v, 0.f, 0.f, 0.f);
+ if (iter == mValue.end() || shouldChange(iter->second, vec))
+ {
+ glUniform1f(location, v);
+ mValue[location] = vec;
+ }
+ }
+}
+
+void LLGLSLShader::uniform2f(const LLStaticHashedString& uniform, GLfloat x, GLfloat y)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ GLint location = getUniformLocation(uniform);
+
+ if (location >= 0)
+ {
+ const auto& iter = mValue.find(location);
+ LLVector4 vec(x, y, 0.f, 0.f);
+ if (iter == mValue.end() || shouldChange(iter->second, vec))
+ {
+ glUniform2f(location, x, y);
+ mValue[location] = vec;
+ }
+ }
+
+}
+
+void LLGLSLShader::uniform3f(const LLStaticHashedString& uniform, GLfloat x, GLfloat y, GLfloat z)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ GLint location = getUniformLocation(uniform);
+
+ if (location >= 0)
+ {
+ const auto& iter = mValue.find(location);
+ LLVector4 vec(x, y, z, 0.f);
+ if (iter == mValue.end() || shouldChange(iter->second, vec))
+ {
+ glUniform3f(location, x, y, z);
+ mValue[location] = vec;
+ }
+ }
+}
+
+void LLGLSLShader::uniform1fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ GLint location = getUniformLocation(uniform);
+
+ if (location >= 0)
+ {
+ const auto& iter = mValue.find(location);
+ LLVector4 vec(v[0], 0.f, 0.f, 0.f);
+ if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1)
+ {
+ glUniform1fv(location, count, v);
+ mValue[location] = vec;
+ }
+ }
+}
+
+void LLGLSLShader::uniform2fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ GLint location = getUniformLocation(uniform);
+
+ if (location >= 0)
+ {
+ const auto& iter = mValue.find(location);
+ LLVector4 vec(v[0], v[1], 0.f, 0.f);
+ if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1)
+ {
+ glUniform2fv(location, count, v);
+ mValue[location] = vec;
+ }
+ }
+}
+
+void LLGLSLShader::uniform3fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ GLint location = getUniformLocation(uniform);
+
+ if (location >= 0)
+ {
+ const auto& iter = mValue.find(location);
+ LLVector4 vec(v[0], v[1], v[2], 0.f);
+ if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1)
+ {
+ glUniform3fv(location, count, v);
+ mValue[location] = vec;
+ }
+ }
+}
+
+void LLGLSLShader::uniform4fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ GLint location = getUniformLocation(uniform);
+
+ if (location >= 0)
+ {
+ LLVector4 vec(v);
+ const auto& iter = mValue.find(location);
+ if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ glUniform4fv(location, count, v);
+ mValue[location] = vec;
+ }
+ }
+}
+
+void LLGLSLShader::uniformMatrix4fv(const LLStaticHashedString& uniform, U32 count, GLboolean transpose, const GLfloat* v)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ GLint location = getUniformLocation(uniform);
+
+ if (location >= 0)
+ {
+ stop_glerror();
+ glUniformMatrix4fv(location, count, transpose, v);
+ stop_glerror();
+ }
+}
+
+
+void LLGLSLShader::vertexAttrib4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+ if (mAttribute[index] > 0)
+ {
+ glVertexAttrib4f(mAttribute[index], x, y, z, w);
+ }
+}
+
+void LLGLSLShader::vertexAttrib4fv(U32 index, GLfloat* v)
+{
+ if (mAttribute[index] > 0)
+ {
+ glVertexAttrib4fv(mAttribute[index], v);
+ }
+}
+
+void LLGLSLShader::setMinimumAlpha(F32 minimum)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ gGL.flush();
+ uniform1f(LLShaderMgr::MINIMUM_ALPHA, minimum);
+}
+
+void LLShaderUniforms::apply(LLGLSLShader* shader)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ for (auto& uniform : mIntegers)
+ {
+ shader->uniform1i(uniform.mUniform, uniform.mValue);
+ }
+
+ for (auto& uniform : mFloats)
+ {
+ shader->uniform1f(uniform.mUniform, uniform.mValue);
+ }
+
+ for (auto& uniform : mVectors)
+ {
+ shader->uniform4fv(uniform.mUniform, 1, uniform.mValue.mV);
+ }
+
+ for (auto& uniform : mVector3s)
+ {
+ shader->uniform3fv(uniform.mUniform, 1, uniform.mValue.mV);
+ }
+}
+
+LLUUID LLGLSLShader::hash()
+{
+ HBXXH128 hash_obj;
+ hash_obj.update(mName);
+ hash_obj.update(&mShaderGroup, sizeof(mShaderGroup));
+ hash_obj.update(&mShaderLevel, sizeof(mShaderLevel));
+ for (const auto& shdr_pair : mShaderFiles)
+ {
+ hash_obj.update(shdr_pair.first);
+ hash_obj.update(&shdr_pair.second, sizeof(GLenum));
+ }
+ for (const auto& define_pair : mDefines)
+ {
+ hash_obj.update(define_pair.first);
+ hash_obj.update(define_pair.second);
+
+ }
+ for (const auto& define_pair : LLGLSLShader::sGlobalDefines)
+ {
+ hash_obj.update(define_pair.first);
+ hash_obj.update(define_pair.second);
+
+ }
+ hash_obj.update(&mFeatures, sizeof(LLShaderFeatures));
+ hash_obj.update(gGLManager.mGLVendor);
+ hash_obj.update(gGLManager.mGLRenderer);
+ hash_obj.update(gGLManager.mGLVersionString);
+ return hash_obj.digest();
+}
+
+#ifdef LL_PROFILER_ENABLE_RENDER_DOC
+void LLGLSLShader::setLabel(const char* label) {
+ LL_LABEL_OBJECT_GL(GL_PROGRAM, mProgramObject, strlen(label), label);
+}
+#endif
diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h
index 0148323df5..95e8f1168a 100644
--- a/indra/llrender/llglslshader.h
+++ b/indra/llrender/llglslshader.h
@@ -1,344 +1,344 @@
-/**
- * @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"
-#include "llstaticstringtable.h"
-#include <unordered_map>
-
-class LLShaderFeatures
-{
-public:
- S32 mIndexedTextureChannels = 0;
- bool calculatesLighting = false;
- bool calculatesAtmospherics = false;
- bool hasLighting = false; // implies no transport (it's possible to have neither though)
- bool isAlphaLighting = false; // indicates lighting shaders need not be linked in (lighting performed directly in alpha shader to match deferred lighting functions)
- bool isSpecular = false;
- bool hasTransport = false; // implies no lighting (it's possible to have neither though)
- bool hasSkinning = false;
- bool hasObjectSkinning = false;
- bool hasAtmospherics = false;
- bool hasGamma = false;
- bool hasShadows = false;
- bool hasAmbientOcclusion = false;
- bool hasSrgb = false;
- bool encodesNormal = false; // include: shaders\class1\environment\encodeNormF.glsl
- bool isDeferred = false;
- bool hasScreenSpaceReflections = false;
- bool disableTextureIndex = false;
- bool hasAlphaMask = false;
- bool hasReflectionProbes = false;
- bool attachNothing = false;
-};
-
-// ============= Structure for caching shader uniforms ===============
-class LLGLSLShader;
-
-class LLShaderUniforms
-{
-public:
-
- template<typename T>
- struct UniformSetting
- {
- S32 mUniform;
- T mValue;
- };
-
- typedef UniformSetting<S32> IntSetting;
- typedef UniformSetting<F32> FloatSetting;
- typedef UniformSetting<LLVector4> VectorSetting;
- typedef UniformSetting<LLVector3> Vector3Setting;
-
- void clear()
- {
- mIntegers.resize(0);
- mFloats.resize(0);
- mVectors.resize(0);
- mVector3s.resize(0);
- }
-
- void uniform1i(S32 index, S32 value)
- {
- mIntegers.push_back({ index, value });
- }
-
- void uniform1f(S32 index, F32 value)
- {
- mFloats.push_back({ index, value });
- }
-
- void uniform4fv(S32 index, const LLVector4& value)
- {
- mVectors.push_back({ index, value });
- }
-
- void uniform4fv(S32 index, const F32* value)
- {
- mVectors.push_back({ index, LLVector4(value) });
- }
-
- void uniform3fv(S32 index, const LLVector3& value)
- {
- mVector3s.push_back({ index, value });
- }
-
- void uniform3fv(S32 index, const F32* value)
- {
- mVector3s.push_back({ index, LLVector3(value) });
- }
-
- void apply(LLGLSLShader* shader);
-
-
- std::vector<IntSetting> mIntegers;
- std::vector<FloatSetting> mFloats;
- std::vector<VectorSetting> mVectors;
- std::vector<Vector3Setting> mVector3s;
-};
-class LLGLSLShader
-{
-public:
- // NOTE: Keep gShaderConsts and LLGLSLShader::ShaderConsts_e in sync!
- enum eShaderConsts
- {
- SHADER_CONST_CLOUD_MOON_DEPTH
- , SHADER_CONST_STAR_DEPTH
- , NUM_SHADER_CONSTS
- };
-
- // enum primarily used to control application sky settings uniforms
- typedef enum
- {
- SG_DEFAULT = 0, // not sky or water specific
- SG_SKY, //
- SG_WATER,
- SG_ANY,
- SG_COUNT
- } eGroup;
-
- static std::set<LLGLSLShader*> sInstances;
- static bool sProfileEnabled;
-
- LLGLSLShader();
- ~LLGLSLShader();
-
- static GLuint sCurBoundShader;
- static LLGLSLShader* sCurBoundShaderPtr;
- static S32 sIndexedTextureChannels;
-
- static void initProfile();
- static void finishProfile(bool emit_report = true);
-
- static void startProfile();
- static void stopProfile();
-
- void unload();
- void clearStats();
- void dumpStats();
-
- // place query objects for profiling if profiling is enabled
- // if for_runtime is true, will place timer query only whether or not profiling is enabled
- void placeProfileQuery(bool for_runtime = false);
-
- // Readback query objects if profiling is enabled
- // If for_runtime is true, will readback timer query iff query is available
- // Will return false if a query is pending (try again later)
- // If force_read is true, will force an immediate readback (severe performance penalty)
- bool readProfileQuery(bool for_runtime = false, bool force_read = false);
-
- bool createShader(std::vector<LLStaticHashedString>* attributes,
- std::vector<LLStaticHashedString>* uniforms,
- U32 varying_count = 0,
- const char** varyings = NULL);
- bool attachFragmentObject(std::string object);
- bool attachVertexObject(std::string object);
- void attachObject(GLuint object);
- void attachObjects(GLuint* objects = NULL, S32 count = 0);
- bool mapAttributes(const std::vector<LLStaticHashedString>* attributes);
- bool mapUniforms(const std::vector<LLStaticHashedString>*);
- void mapUniform(GLint index, const std::vector<LLStaticHashedString>*);
- void uniform1i(U32 index, GLint i);
- void uniform1f(U32 index, GLfloat v);
- void fastUniform1f(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 uniform4iv(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 uniform2i(const LLStaticHashedString& uniform, GLint i, GLint j);
- void uniformMatrix2fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v);
- void uniformMatrix3fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v);
- void uniformMatrix3x4fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v);
- void uniformMatrix4fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v);
- void uniform1i(const LLStaticHashedString& uniform, GLint i);
- void uniform1iv(const LLStaticHashedString& uniform, U32 count, const GLint* v);
- void uniform4iv(const LLStaticHashedString& uniform, U32 count, const GLint* v);
- void uniform1f(const LLStaticHashedString& uniform, GLfloat v);
- void uniform2f(const LLStaticHashedString& uniform, GLfloat x, GLfloat y);
- void uniform3f(const LLStaticHashedString& uniform, GLfloat x, GLfloat y, GLfloat z);
- void uniform1fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v);
- void uniform2fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v);
- void uniform3fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v);
- void uniform4fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v);
- void uniformMatrix4fv(const LLStaticHashedString& uniform, U32 count, GLboolean transpose, const GLfloat* v);
-
- void setMinimumAlpha(F32 minimum);
-
- 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 getUniformLocation(const LLStaticHashedString& uniform);
- GLint getUniformLocation(U32 index);
-
- GLint getAttribLocation(U32 attrib);
- GLint mapUniformTextureChannel(GLint location, GLenum type, GLint size);
-
- void clearPermutations();
- void addPermutation(std::string name, std::string value);
- void removePermutation(std::string name);
-
- void addConstant(const LLGLSLShader::eShaderConsts shader_const);
-
- //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, LLTexUnit::eTextureColorSpace space = LLTexUnit::TCS_LINEAR);
- S32 disableTexture(S32 uniform, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE, LLTexUnit::eTextureColorSpace space = LLTexUnit::TCS_LINEAR);
-
- // get the texture channel of the given uniform, or -1 if uniform is not used as a texture
- S32 getTextureChannel(S32 uniform) const;
-
- // bindTexture returns the texture unit we've bound the texture to.
- // You can reuse the return value to unbind a texture when required.
- S32 bindTexture(const std::string& uniform, LLTexture* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE, LLTexUnit::eTextureColorSpace space = LLTexUnit::TCS_LINEAR);
- S32 bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE, LLTexUnit::eTextureColorSpace space = LLTexUnit::TCS_LINEAR);
- S32 bindTexture(const std::string& uniform, LLRenderTarget* texture, bool depth = false, LLTexUnit::eTextureFilterOptions mode = LLTexUnit::TFO_BILINEAR);
- S32 bindTexture(S32 uniform, LLRenderTarget* texture, bool depth = false, LLTexUnit::eTextureFilterOptions mode = LLTexUnit::TFO_BILINEAR, U32 index = 0);
- S32 unbindTexture(const std::string& uniform, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE);
- S32 unbindTexture(S32 uniform, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE);
-
- bool link(bool suppress_errors = false);
- void bind();
- //helper to conditionally bind mRiggedVariant instead of this
- void bind(bool rigged);
-
- bool isComplete() const { return mProgramObject != 0; }
-
- LLUUID hash();
-
- // Unbinds any previously bound shader by explicitly binding no shader.
- static void unbind();
-
- U32 mMatHash[LLRender::NUM_MATRIX_MODES];
- U32 mLightHash;
-
- GLuint mProgramObject;
-#if LL_RELEASE_WITH_DEBUG_INFO
- struct attr_name
- {
- GLint loc;
- const char* name;
- void operator = (GLint _loc) { loc = _loc; }
- operator GLint () { return loc; }
- };
- std::vector<attr_name> mAttribute; //lookup table of attribute enum to attribute channel
-#else
- std::vector<GLint> mAttribute; //lookup table of attribute enum to attribute channel
-#endif
- U32 mAttributeMask; //mask of which reserved attributes are set (lines up with LLVertexBuffer::getTypeMask())
- std::vector<GLint> mUniform; //lookup table of uniform enum to uniform location
- LLStaticStringTable<GLint> mUniformMap; //lookup map of uniform name to uniform location
- typedef std::unordered_map<GLint, LLVector4> uniform_value_map_t;
- uniform_value_map_t mValue; //lookup map of uniform location to last known value
- std::vector<GLint> mTexture;
- S32 mTotalUniformSize;
- S32 mActiveTextureChannels;
- S32 mShaderLevel;
- S32 mShaderGroup; // see LLGLSLShader::eGroup
- bool mUniformsDirty;
- LLShaderFeatures mFeatures;
- std::vector< std::pair< std::string, GLenum > > mShaderFiles;
- std::string mName;
- typedef std::map<std::string, std::string> defines_map_t; //NOTE: this must be an ordered map to maintain hash consistency
- defines_map_t mDefines;
- static defines_map_t sGlobalDefines;
- LLUUID mShaderHash;
- bool mUsingBinaryProgram = false;
-
- //statistics for profiling shader performance
- bool mProfilePending = false;
- U32 mTimerQuery;
- U32 mSamplesQuery;
- U32 mPrimitivesQuery;
-
- U64 mTimeElapsed;
- static U64 sTotalTimeElapsed;
- U32 mTrianglesDrawn;
- static U32 sTotalTrianglesDrawn;
- U64 mSamplesDrawn;
- static U64 sTotalSamplesDrawn;
- U32 mBinds;
- static U32 sTotalBinds;
-
- // this pointer should be set to whichever shader represents this shader's rigged variant
- LLGLSLShader* mRiggedVariant = nullptr;
-
- // hacky flag used for optimization in LLDrawPoolAlpha
- bool mCanBindFast = false;
-
-#ifdef LL_PROFILER_ENABLE_RENDER_DOC
- void setLabel(const char* label);
-#endif
-
-private:
- void unloadInternal();
-};
-
-//UI shader (declared here so llui_libtest will link properly)
-extern LLGLSLShader gUIProgram;
-//output vec4(color.rgb,color.a*tex0[tc0].a)
-extern LLGLSLShader gSolidColorProgram;
-//Alpha mask shader (declared here so llappearance can access properly)
-extern LLGLSLShader gAlphaMaskProgram;
-
-#ifdef LL_PROFILER_ENABLE_RENDER_DOC
-#define LL_SET_SHADER_LABEL(shader) shader.setLabel(#shader)
-#else
-#define LL_SET_SHADER_LABEL(shader, label)
-#endif
-
-#endif
+/**
+ * @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"
+#include "llstaticstringtable.h"
+#include <unordered_map>
+
+class LLShaderFeatures
+{
+public:
+ S32 mIndexedTextureChannels = 0;
+ bool calculatesLighting = false;
+ bool calculatesAtmospherics = false;
+ bool hasLighting = false; // implies no transport (it's possible to have neither though)
+ bool isAlphaLighting = false; // indicates lighting shaders need not be linked in (lighting performed directly in alpha shader to match deferred lighting functions)
+ bool isSpecular = false;
+ bool hasTransport = false; // implies no lighting (it's possible to have neither though)
+ bool hasSkinning = false;
+ bool hasObjectSkinning = false;
+ bool hasAtmospherics = false;
+ bool hasGamma = false;
+ bool hasShadows = false;
+ bool hasAmbientOcclusion = false;
+ bool hasSrgb = false;
+ bool encodesNormal = false; // include: shaders\class1\environment\encodeNormF.glsl
+ bool isDeferred = false;
+ bool hasScreenSpaceReflections = false;
+ bool disableTextureIndex = false;
+ bool hasAlphaMask = false;
+ bool hasReflectionProbes = false;
+ bool attachNothing = false;
+};
+
+// ============= Structure for caching shader uniforms ===============
+class LLGLSLShader;
+
+class LLShaderUniforms
+{
+public:
+
+ template<typename T>
+ struct UniformSetting
+ {
+ S32 mUniform;
+ T mValue;
+ };
+
+ typedef UniformSetting<S32> IntSetting;
+ typedef UniformSetting<F32> FloatSetting;
+ typedef UniformSetting<LLVector4> VectorSetting;
+ typedef UniformSetting<LLVector3> Vector3Setting;
+
+ void clear()
+ {
+ mIntegers.resize(0);
+ mFloats.resize(0);
+ mVectors.resize(0);
+ mVector3s.resize(0);
+ }
+
+ void uniform1i(S32 index, S32 value)
+ {
+ mIntegers.push_back({ index, value });
+ }
+
+ void uniform1f(S32 index, F32 value)
+ {
+ mFloats.push_back({ index, value });
+ }
+
+ void uniform4fv(S32 index, const LLVector4& value)
+ {
+ mVectors.push_back({ index, value });
+ }
+
+ void uniform4fv(S32 index, const F32* value)
+ {
+ mVectors.push_back({ index, LLVector4(value) });
+ }
+
+ void uniform3fv(S32 index, const LLVector3& value)
+ {
+ mVector3s.push_back({ index, value });
+ }
+
+ void uniform3fv(S32 index, const F32* value)
+ {
+ mVector3s.push_back({ index, LLVector3(value) });
+ }
+
+ void apply(LLGLSLShader* shader);
+
+
+ std::vector<IntSetting> mIntegers;
+ std::vector<FloatSetting> mFloats;
+ std::vector<VectorSetting> mVectors;
+ std::vector<Vector3Setting> mVector3s;
+};
+class LLGLSLShader
+{
+public:
+ // NOTE: Keep gShaderConsts and LLGLSLShader::ShaderConsts_e in sync!
+ enum eShaderConsts
+ {
+ SHADER_CONST_CLOUD_MOON_DEPTH
+ , SHADER_CONST_STAR_DEPTH
+ , NUM_SHADER_CONSTS
+ };
+
+ // enum primarily used to control application sky settings uniforms
+ typedef enum
+ {
+ SG_DEFAULT = 0, // not sky or water specific
+ SG_SKY, //
+ SG_WATER,
+ SG_ANY,
+ SG_COUNT
+ } eGroup;
+
+ static std::set<LLGLSLShader*> sInstances;
+ static bool sProfileEnabled;
+
+ LLGLSLShader();
+ ~LLGLSLShader();
+
+ static GLuint sCurBoundShader;
+ static LLGLSLShader* sCurBoundShaderPtr;
+ static S32 sIndexedTextureChannels;
+
+ static void initProfile();
+ static void finishProfile(bool emit_report = true);
+
+ static void startProfile();
+ static void stopProfile();
+
+ void unload();
+ void clearStats();
+ void dumpStats();
+
+ // place query objects for profiling if profiling is enabled
+ // if for_runtime is true, will place timer query only whether or not profiling is enabled
+ void placeProfileQuery(bool for_runtime = false);
+
+ // Readback query objects if profiling is enabled
+ // If for_runtime is true, will readback timer query iff query is available
+ // Will return false if a query is pending (try again later)
+ // If force_read is true, will force an immediate readback (severe performance penalty)
+ bool readProfileQuery(bool for_runtime = false, bool force_read = false);
+
+ bool createShader(std::vector<LLStaticHashedString>* attributes,
+ std::vector<LLStaticHashedString>* uniforms,
+ U32 varying_count = 0,
+ const char** varyings = NULL);
+ bool attachFragmentObject(std::string object);
+ bool attachVertexObject(std::string object);
+ void attachObject(GLuint object);
+ void attachObjects(GLuint* objects = NULL, S32 count = 0);
+ bool mapAttributes(const std::vector<LLStaticHashedString>* attributes);
+ bool mapUniforms(const std::vector<LLStaticHashedString>*);
+ void mapUniform(GLint index, const std::vector<LLStaticHashedString>*);
+ void uniform1i(U32 index, GLint i);
+ void uniform1f(U32 index, GLfloat v);
+ void fastUniform1f(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 uniform4iv(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 uniform2i(const LLStaticHashedString& uniform, GLint i, GLint j);
+ void uniformMatrix2fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v);
+ void uniformMatrix3fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v);
+ void uniformMatrix3x4fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v);
+ void uniformMatrix4fv(U32 index, U32 count, GLboolean transpose, const GLfloat* v);
+ void uniform1i(const LLStaticHashedString& uniform, GLint i);
+ void uniform1iv(const LLStaticHashedString& uniform, U32 count, const GLint* v);
+ void uniform4iv(const LLStaticHashedString& uniform, U32 count, const GLint* v);
+ void uniform1f(const LLStaticHashedString& uniform, GLfloat v);
+ void uniform2f(const LLStaticHashedString& uniform, GLfloat x, GLfloat y);
+ void uniform3f(const LLStaticHashedString& uniform, GLfloat x, GLfloat y, GLfloat z);
+ void uniform1fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v);
+ void uniform2fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v);
+ void uniform3fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v);
+ void uniform4fv(const LLStaticHashedString& uniform, U32 count, const GLfloat* v);
+ void uniformMatrix4fv(const LLStaticHashedString& uniform, U32 count, GLboolean transpose, const GLfloat* v);
+
+ void setMinimumAlpha(F32 minimum);
+
+ 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 getUniformLocation(const LLStaticHashedString& uniform);
+ GLint getUniformLocation(U32 index);
+
+ GLint getAttribLocation(U32 attrib);
+ GLint mapUniformTextureChannel(GLint location, GLenum type, GLint size);
+
+ void clearPermutations();
+ void addPermutation(std::string name, std::string value);
+ void removePermutation(std::string name);
+
+ void addConstant(const LLGLSLShader::eShaderConsts shader_const);
+
+ //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, LLTexUnit::eTextureColorSpace space = LLTexUnit::TCS_LINEAR);
+ S32 disableTexture(S32 uniform, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE, LLTexUnit::eTextureColorSpace space = LLTexUnit::TCS_LINEAR);
+
+ // get the texture channel of the given uniform, or -1 if uniform is not used as a texture
+ S32 getTextureChannel(S32 uniform) const;
+
+ // bindTexture returns the texture unit we've bound the texture to.
+ // You can reuse the return value to unbind a texture when required.
+ S32 bindTexture(const std::string& uniform, LLTexture* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE, LLTexUnit::eTextureColorSpace space = LLTexUnit::TCS_LINEAR);
+ S32 bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE, LLTexUnit::eTextureColorSpace space = LLTexUnit::TCS_LINEAR);
+ S32 bindTexture(const std::string& uniform, LLRenderTarget* texture, bool depth = false, LLTexUnit::eTextureFilterOptions mode = LLTexUnit::TFO_BILINEAR);
+ S32 bindTexture(S32 uniform, LLRenderTarget* texture, bool depth = false, LLTexUnit::eTextureFilterOptions mode = LLTexUnit::TFO_BILINEAR, U32 index = 0);
+ S32 unbindTexture(const std::string& uniform, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE);
+ S32 unbindTexture(S32 uniform, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE);
+
+ bool link(bool suppress_errors = false);
+ void bind();
+ //helper to conditionally bind mRiggedVariant instead of this
+ void bind(bool rigged);
+
+ bool isComplete() const { return mProgramObject != 0; }
+
+ LLUUID hash();
+
+ // Unbinds any previously bound shader by explicitly binding no shader.
+ static void unbind();
+
+ U32 mMatHash[LLRender::NUM_MATRIX_MODES];
+ U32 mLightHash;
+
+ GLuint mProgramObject;
+#if LL_RELEASE_WITH_DEBUG_INFO
+ struct attr_name
+ {
+ GLint loc;
+ const char* name;
+ void operator = (GLint _loc) { loc = _loc; }
+ operator GLint () { return loc; }
+ };
+ std::vector<attr_name> mAttribute; //lookup table of attribute enum to attribute channel
+#else
+ std::vector<GLint> mAttribute; //lookup table of attribute enum to attribute channel
+#endif
+ U32 mAttributeMask; //mask of which reserved attributes are set (lines up with LLVertexBuffer::getTypeMask())
+ std::vector<GLint> mUniform; //lookup table of uniform enum to uniform location
+ LLStaticStringTable<GLint> mUniformMap; //lookup map of uniform name to uniform location
+ typedef std::unordered_map<GLint, LLVector4> uniform_value_map_t;
+ uniform_value_map_t mValue; //lookup map of uniform location to last known value
+ std::vector<GLint> mTexture;
+ S32 mTotalUniformSize;
+ S32 mActiveTextureChannels;
+ S32 mShaderLevel;
+ S32 mShaderGroup; // see LLGLSLShader::eGroup
+ bool mUniformsDirty;
+ LLShaderFeatures mFeatures;
+ std::vector< std::pair< std::string, GLenum > > mShaderFiles;
+ std::string mName;
+ typedef std::map<std::string, std::string> defines_map_t; //NOTE: this must be an ordered map to maintain hash consistency
+ defines_map_t mDefines;
+ static defines_map_t sGlobalDefines;
+ LLUUID mShaderHash;
+ bool mUsingBinaryProgram = false;
+
+ //statistics for profiling shader performance
+ bool mProfilePending = false;
+ U32 mTimerQuery;
+ U32 mSamplesQuery;
+ U32 mPrimitivesQuery;
+
+ U64 mTimeElapsed;
+ static U64 sTotalTimeElapsed;
+ U32 mTrianglesDrawn;
+ static U32 sTotalTrianglesDrawn;
+ U64 mSamplesDrawn;
+ static U64 sTotalSamplesDrawn;
+ U32 mBinds;
+ static U32 sTotalBinds;
+
+ // this pointer should be set to whichever shader represents this shader's rigged variant
+ LLGLSLShader* mRiggedVariant = nullptr;
+
+ // hacky flag used for optimization in LLDrawPoolAlpha
+ bool mCanBindFast = false;
+
+#ifdef LL_PROFILER_ENABLE_RENDER_DOC
+ void setLabel(const char* label);
+#endif
+
+private:
+ void unloadInternal();
+};
+
+//UI shader (declared here so llui_libtest will link properly)
+extern LLGLSLShader gUIProgram;
+//output vec4(color.rgb,color.a*tex0[tc0].a)
+extern LLGLSLShader gSolidColorProgram;
+//Alpha mask shader (declared here so llappearance can access properly)
+extern LLGLSLShader gAlphaMaskProgram;
+
+#ifdef LL_PROFILER_ENABLE_RENDER_DOC
+#define LL_SET_SHADER_LABEL(shader) shader.setLabel(#shader)
+#else
+#define LL_SET_SHADER_LABEL(shader, label)
+#endif
+
+#endif
diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp
index b2660f8481..15d9bcc4eb 100644
--- a/indra/llrender/llgltexture.cpp
+++ b/indra/llrender/llgltexture.cpp
@@ -1,395 +1,395 @@
-/**
- * @file llgltexture.cpp
- * @brief Opengl texture implementation
- *
- * $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 "llgltexture.h"
-
-
-LLGLTexture::LLGLTexture(bool usemipmaps)
-{
- init();
- mUseMipMaps = usemipmaps;
-}
-
-LLGLTexture::LLGLTexture(const U32 width, const U32 height, const U8 components, bool usemipmaps)
-{
- init();
- mFullWidth = width ;
- mFullHeight = height ;
- mUseMipMaps = usemipmaps;
- mComponents = components ;
- setTexelsPerImage();
-}
-
-LLGLTexture::LLGLTexture(const LLImageRaw* raw, bool usemipmaps)
-{
- init();
- mUseMipMaps = usemipmaps ;
- // Create an empty image of the specified size and width
- mGLTexturep = new LLImageGL(raw, usemipmaps) ;
-}
-
-LLGLTexture::~LLGLTexture()
-{
- cleanup();
-}
-
-void LLGLTexture::init()
-{
- mBoostLevel = LLGLTexture::BOOST_NONE;
-
- mFullWidth = 0;
- mFullHeight = 0;
- mTexelsPerImage = 0 ;
- mUseMipMaps = false ;
- mComponents = 0 ;
-
- mTextureState = NO_DELETE ;
- mDontDiscard = false;
- mNeedsGLTexture = false ;
-}
-
-void LLGLTexture::cleanup()
-{
- if(mGLTexturep)
- {
- mGLTexturep->cleanup();
- }
-}
-
-// virtual
-void LLGLTexture::dump()
-{
- if(mGLTexturep)
- {
- mGLTexturep->dump();
- }
-}
-
-void LLGLTexture::setBoostLevel(S32 level)
-{
- if(mBoostLevel != level)
- {
- mBoostLevel = level ;
- if(mBoostLevel != LLGLTexture::BOOST_NONE
- && mBoostLevel != LLGLTexture::BOOST_ICON
- && mBoostLevel != LLGLTexture::BOOST_THUMBNAIL)
- {
- setNoDelete() ;
- }
- }
-}
-
-void LLGLTexture::forceActive()
-{
- mTextureState = ACTIVE ;
-}
-
-void LLGLTexture::setActive()
-{
- if(mTextureState != NO_DELETE)
- {
- mTextureState = ACTIVE ;
- }
-}
-
-//set the texture to stay in memory
-void LLGLTexture::setNoDelete()
-{
- mTextureState = NO_DELETE ;
-}
-
-void LLGLTexture::generateGLTexture()
-{
- if(mGLTexturep.isNull())
- {
- mGLTexturep = new LLImageGL(mFullWidth, mFullHeight, mComponents, mUseMipMaps) ;
- }
-}
-
-LLImageGL* LLGLTexture::getGLTexture() const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep ;
-}
-
-bool LLGLTexture::createGLTexture()
-{
- if(mGLTexturep.isNull())
- {
- generateGLTexture() ;
- }
-
- return mGLTexturep->createGLTexture() ;
-}
-
-bool LLGLTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename, bool to_create, S32 category, bool defer_copy, LLGLuint* tex_name)
-{
- llassert(mGLTexturep.notNull());
-
- bool ret = mGLTexturep->createGLTexture(discard_level, imageraw, usename, to_create, category, defer_copy, tex_name) ;
-
- if(ret)
- {
- mFullWidth = mGLTexturep->getCurrentWidth() ;
- mFullHeight = mGLTexturep->getCurrentHeight() ;
- mComponents = mGLTexturep->getComponents() ;
- setTexelsPerImage();
- }
-
- return ret ;
-}
-
-void LLGLTexture::setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, bool swap_bytes)
-{
- llassert(mGLTexturep.notNull()) ;
-
- mGLTexturep->setExplicitFormat(internal_format, primary_format, type_format, swap_bytes) ;
-}
-void LLGLTexture::setAddressMode(LLTexUnit::eTextureAddressMode mode)
-{
- llassert(mGLTexturep.notNull()) ;
- mGLTexturep->setAddressMode(mode) ;
-}
-void LLGLTexture::setFilteringOption(LLTexUnit::eTextureFilterOptions option)
-{
- llassert(mGLTexturep.notNull()) ;
- mGLTexturep->setFilteringOption(option) ;
-}
-
-//virtual
-S32 LLGLTexture::getWidth(S32 discard_level) const
-{
- llassert(mGLTexturep.notNull()) ;
- return mGLTexturep->getWidth(discard_level) ;
-}
-
-//virtual
-S32 LLGLTexture::getHeight(S32 discard_level) const
-{
- llassert(mGLTexturep.notNull()) ;
- return mGLTexturep->getHeight(discard_level) ;
-}
-
-S32 LLGLTexture::getMaxDiscardLevel() const
-{
- llassert(mGLTexturep.notNull()) ;
- return mGLTexturep->getMaxDiscardLevel() ;
-}
-S32 LLGLTexture::getDiscardLevel() const
-{
- llassert(mGLTexturep.notNull()) ;
- return mGLTexturep->getDiscardLevel() ;
-}
-S8 LLGLTexture::getComponents() const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->getComponents() ;
-}
-
-LLGLuint LLGLTexture::getTexName() const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->getTexName() ;
-}
-
-bool LLGLTexture::hasGLTexture() const
-{
- if(mGLTexturep.notNull())
- {
- return mGLTexturep->getHasGLTexture() ;
- }
- return false ;
-}
-
-bool LLGLTexture::getBoundRecently() const
-{
- if(mGLTexturep.notNull())
- {
- return mGLTexturep->getBoundRecently() ;
- }
- return false ;
-}
-
-LLTexUnit::eTextureType LLGLTexture::getTarget(void) const
-{
- llassert(mGLTexturep.notNull()) ;
- return mGLTexturep->getTarget() ;
-}
-
-bool LLGLTexture::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, LLGLuint use_name)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->setSubImage(imageraw, x_pos, y_pos, width, height, 0, use_name) ;
-}
-
-bool LLGLTexture::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, LLGLuint use_name)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->setSubImage(datap, data_width, data_height, x_pos, y_pos, width, height, 0, use_name) ;
-}
-
-void LLGLTexture::setGLTextureCreated (bool initialized)
-{
- llassert(mGLTexturep.notNull()) ;
-
- mGLTexturep->setGLTextureCreated (initialized) ;
-}
-
-void LLGLTexture::setCategory(S32 category)
-{
- llassert(mGLTexturep.notNull()) ;
-
- mGLTexturep->setCategory(category) ;
-}
-
-void LLGLTexture::setTexName(LLGLuint texName)
-{
- llassert(mGLTexturep.notNull());
- return mGLTexturep->setTexName(texName);
-}
-
-void LLGLTexture::setTarget(const LLGLenum target, const LLTexUnit::eTextureType bind_target)
-{
- llassert(mGLTexturep.notNull());
- return mGLTexturep->setTarget(target, bind_target);
-}
-
-LLTexUnit::eTextureAddressMode LLGLTexture::getAddressMode(void) const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->getAddressMode() ;
-}
-
-S32Bytes LLGLTexture::getTextureMemory() const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->mTextureMemory ;
-}
-
-LLGLenum LLGLTexture::getPrimaryFormat() const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->getPrimaryFormat() ;
-}
-
-bool LLGLTexture::getIsAlphaMask() const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->getIsAlphaMask() ;
-}
-
-bool LLGLTexture::getMask(const LLVector2 &tc)
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->getMask(tc) ;
-}
-
-F32 LLGLTexture::getTimePassedSinceLastBound()
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->getTimePassedSinceLastBound() ;
-}
-bool LLGLTexture::getMissed() const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->getMissed() ;
-}
-
-bool LLGLTexture::isJustBound() const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->isJustBound() ;
-}
-
-void LLGLTexture::forceUpdateBindStats(void) const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->forceUpdateBindStats() ;
-}
-
-U32 LLGLTexture::getTexelsInAtlas() const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->getTexelsInAtlas() ;
-}
-
-U32 LLGLTexture::getTexelsInGLTexture() const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->getTexelsInGLTexture() ;
-}
-
-bool LLGLTexture::isGLTextureCreated() const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->isGLTextureCreated() ;
-}
-
-S32 LLGLTexture::getDiscardLevelInAtlas() const
-{
- llassert(mGLTexturep.notNull()) ;
-
- return mGLTexturep->getDiscardLevelInAtlas() ;
-}
-
-void LLGLTexture::destroyGLTexture()
-{
- if(mGLTexturep.notNull() && mGLTexturep->getHasGLTexture())
- {
- mGLTexturep->destroyGLTexture() ;
- mTextureState = DELETED ;
- }
-}
-
-void LLGLTexture::setTexelsPerImage()
-{
- U32 fullwidth = llmin(mFullWidth,U32(MAX_IMAGE_SIZE_DEFAULT));
- U32 fullheight = llmin(mFullHeight,U32(MAX_IMAGE_SIZE_DEFAULT));
- mTexelsPerImage = (U32)fullwidth * fullheight;
-}
-
-static LLUUID sStubUUID;
-
-const LLUUID& LLGLTexture::getID() const { return sStubUUID; }
+/**
+ * @file llgltexture.cpp
+ * @brief Opengl texture implementation
+ *
+ * $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 "llgltexture.h"
+
+
+LLGLTexture::LLGLTexture(bool usemipmaps)
+{
+ init();
+ mUseMipMaps = usemipmaps;
+}
+
+LLGLTexture::LLGLTexture(const U32 width, const U32 height, const U8 components, bool usemipmaps)
+{
+ init();
+ mFullWidth = width ;
+ mFullHeight = height ;
+ mUseMipMaps = usemipmaps;
+ mComponents = components ;
+ setTexelsPerImage();
+}
+
+LLGLTexture::LLGLTexture(const LLImageRaw* raw, bool usemipmaps)
+{
+ init();
+ mUseMipMaps = usemipmaps ;
+ // Create an empty image of the specified size and width
+ mGLTexturep = new LLImageGL(raw, usemipmaps) ;
+}
+
+LLGLTexture::~LLGLTexture()
+{
+ cleanup();
+}
+
+void LLGLTexture::init()
+{
+ mBoostLevel = LLGLTexture::BOOST_NONE;
+
+ mFullWidth = 0;
+ mFullHeight = 0;
+ mTexelsPerImage = 0 ;
+ mUseMipMaps = false ;
+ mComponents = 0 ;
+
+ mTextureState = NO_DELETE ;
+ mDontDiscard = false;
+ mNeedsGLTexture = false ;
+}
+
+void LLGLTexture::cleanup()
+{
+ if(mGLTexturep)
+ {
+ mGLTexturep->cleanup();
+ }
+}
+
+// virtual
+void LLGLTexture::dump()
+{
+ if(mGLTexturep)
+ {
+ mGLTexturep->dump();
+ }
+}
+
+void LLGLTexture::setBoostLevel(S32 level)
+{
+ if(mBoostLevel != level)
+ {
+ mBoostLevel = level ;
+ if(mBoostLevel != LLGLTexture::BOOST_NONE
+ && mBoostLevel != LLGLTexture::BOOST_ICON
+ && mBoostLevel != LLGLTexture::BOOST_THUMBNAIL)
+ {
+ setNoDelete() ;
+ }
+ }
+}
+
+void LLGLTexture::forceActive()
+{
+ mTextureState = ACTIVE ;
+}
+
+void LLGLTexture::setActive()
+{
+ if(mTextureState != NO_DELETE)
+ {
+ mTextureState = ACTIVE ;
+ }
+}
+
+//set the texture to stay in memory
+void LLGLTexture::setNoDelete()
+{
+ mTextureState = NO_DELETE ;
+}
+
+void LLGLTexture::generateGLTexture()
+{
+ if(mGLTexturep.isNull())
+ {
+ mGLTexturep = new LLImageGL(mFullWidth, mFullHeight, mComponents, mUseMipMaps) ;
+ }
+}
+
+LLImageGL* LLGLTexture::getGLTexture() const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep ;
+}
+
+bool LLGLTexture::createGLTexture()
+{
+ if(mGLTexturep.isNull())
+ {
+ generateGLTexture() ;
+ }
+
+ return mGLTexturep->createGLTexture() ;
+}
+
+bool LLGLTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename, bool to_create, S32 category, bool defer_copy, LLGLuint* tex_name)
+{
+ llassert(mGLTexturep.notNull());
+
+ bool ret = mGLTexturep->createGLTexture(discard_level, imageraw, usename, to_create, category, defer_copy, tex_name) ;
+
+ if(ret)
+ {
+ mFullWidth = mGLTexturep->getCurrentWidth() ;
+ mFullHeight = mGLTexturep->getCurrentHeight() ;
+ mComponents = mGLTexturep->getComponents() ;
+ setTexelsPerImage();
+ }
+
+ return ret ;
+}
+
+void LLGLTexture::setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, bool swap_bytes)
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ mGLTexturep->setExplicitFormat(internal_format, primary_format, type_format, swap_bytes) ;
+}
+void LLGLTexture::setAddressMode(LLTexUnit::eTextureAddressMode mode)
+{
+ llassert(mGLTexturep.notNull()) ;
+ mGLTexturep->setAddressMode(mode) ;
+}
+void LLGLTexture::setFilteringOption(LLTexUnit::eTextureFilterOptions option)
+{
+ llassert(mGLTexturep.notNull()) ;
+ mGLTexturep->setFilteringOption(option) ;
+}
+
+//virtual
+S32 LLGLTexture::getWidth(S32 discard_level) const
+{
+ llassert(mGLTexturep.notNull()) ;
+ return mGLTexturep->getWidth(discard_level) ;
+}
+
+//virtual
+S32 LLGLTexture::getHeight(S32 discard_level) const
+{
+ llassert(mGLTexturep.notNull()) ;
+ return mGLTexturep->getHeight(discard_level) ;
+}
+
+S32 LLGLTexture::getMaxDiscardLevel() const
+{
+ llassert(mGLTexturep.notNull()) ;
+ return mGLTexturep->getMaxDiscardLevel() ;
+}
+S32 LLGLTexture::getDiscardLevel() const
+{
+ llassert(mGLTexturep.notNull()) ;
+ return mGLTexturep->getDiscardLevel() ;
+}
+S8 LLGLTexture::getComponents() const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->getComponents() ;
+}
+
+LLGLuint LLGLTexture::getTexName() const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->getTexName() ;
+}
+
+bool LLGLTexture::hasGLTexture() const
+{
+ if(mGLTexturep.notNull())
+ {
+ return mGLTexturep->getHasGLTexture() ;
+ }
+ return false ;
+}
+
+bool LLGLTexture::getBoundRecently() const
+{
+ if(mGLTexturep.notNull())
+ {
+ return mGLTexturep->getBoundRecently() ;
+ }
+ return false ;
+}
+
+LLTexUnit::eTextureType LLGLTexture::getTarget(void) const
+{
+ llassert(mGLTexturep.notNull()) ;
+ return mGLTexturep->getTarget() ;
+}
+
+bool LLGLTexture::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, LLGLuint use_name)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->setSubImage(imageraw, x_pos, y_pos, width, height, 0, use_name) ;
+}
+
+bool LLGLTexture::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, LLGLuint use_name)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->setSubImage(datap, data_width, data_height, x_pos, y_pos, width, height, 0, use_name) ;
+}
+
+void LLGLTexture::setGLTextureCreated (bool initialized)
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ mGLTexturep->setGLTextureCreated (initialized) ;
+}
+
+void LLGLTexture::setCategory(S32 category)
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ mGLTexturep->setCategory(category) ;
+}
+
+void LLGLTexture::setTexName(LLGLuint texName)
+{
+ llassert(mGLTexturep.notNull());
+ return mGLTexturep->setTexName(texName);
+}
+
+void LLGLTexture::setTarget(const LLGLenum target, const LLTexUnit::eTextureType bind_target)
+{
+ llassert(mGLTexturep.notNull());
+ return mGLTexturep->setTarget(target, bind_target);
+}
+
+LLTexUnit::eTextureAddressMode LLGLTexture::getAddressMode(void) const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->getAddressMode() ;
+}
+
+S32Bytes LLGLTexture::getTextureMemory() const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->mTextureMemory ;
+}
+
+LLGLenum LLGLTexture::getPrimaryFormat() const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->getPrimaryFormat() ;
+}
+
+bool LLGLTexture::getIsAlphaMask() const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->getIsAlphaMask() ;
+}
+
+bool LLGLTexture::getMask(const LLVector2 &tc)
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->getMask(tc) ;
+}
+
+F32 LLGLTexture::getTimePassedSinceLastBound()
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->getTimePassedSinceLastBound() ;
+}
+bool LLGLTexture::getMissed() const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->getMissed() ;
+}
+
+bool LLGLTexture::isJustBound() const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->isJustBound() ;
+}
+
+void LLGLTexture::forceUpdateBindStats(void) const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->forceUpdateBindStats() ;
+}
+
+U32 LLGLTexture::getTexelsInAtlas() const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->getTexelsInAtlas() ;
+}
+
+U32 LLGLTexture::getTexelsInGLTexture() const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->getTexelsInGLTexture() ;
+}
+
+bool LLGLTexture::isGLTextureCreated() const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->isGLTextureCreated() ;
+}
+
+S32 LLGLTexture::getDiscardLevelInAtlas() const
+{
+ llassert(mGLTexturep.notNull()) ;
+
+ return mGLTexturep->getDiscardLevelInAtlas() ;
+}
+
+void LLGLTexture::destroyGLTexture()
+{
+ if(mGLTexturep.notNull() && mGLTexturep->getHasGLTexture())
+ {
+ mGLTexturep->destroyGLTexture() ;
+ mTextureState = DELETED ;
+ }
+}
+
+void LLGLTexture::setTexelsPerImage()
+{
+ U32 fullwidth = llmin(mFullWidth,U32(MAX_IMAGE_SIZE_DEFAULT));
+ U32 fullheight = llmin(mFullHeight,U32(MAX_IMAGE_SIZE_DEFAULT));
+ mTexelsPerImage = (U32)fullwidth * fullheight;
+}
+
+static LLUUID sStubUUID;
+
+const LLUUID& LLGLTexture::getID() const { return sStubUUID; }
diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h
index 3b2b7c00d9..c942008874 100644
--- a/indra/llrender/llgltexture.h
+++ b/indra/llrender/llgltexture.h
@@ -1,206 +1,206 @@
-/**
- * @file llgltexture.h
- * @brief Object for managing opengl textures
- *
- * $LicenseInfo:firstyear=2012&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_GL_TEXTURE_H
-#define LL_GL_TEXTURE_H
-
-#include "lltexture.h"
-#include "llgl.h"
-
-class LLImageRaw;
-
-//
-//this the parent for the class LLViewerTexture
-//through the following virtual functions, the class LLViewerTexture can be reached from /llrender.
-//
-class LLGLTexture : public LLTexture
-{
-public:
- enum
- {
- MAX_IMAGE_SIZE_DEFAULT = 1024,
- INVALID_DISCARD_LEVEL = 0x7fff
- };
-
- enum EBoostLevel
- {
- BOOST_NONE = 0,
- BOOST_AVATAR ,
- BOOST_AVATAR_BAKED ,
- BOOST_SCULPTED ,
-
- BOOST_HIGH = 10,
- BOOST_BUMP ,
- BOOST_TERRAIN , // has to be high priority for minimap / low detail
- BOOST_SELECTED ,
- BOOST_AVATAR_BAKED_SELF ,
- BOOST_AVATAR_SELF , // needed for baking avatar
- BOOST_SUPER_HIGH , //textures higher than this need to be downloaded at the required resolution without delay.
- BOOST_HUD ,
- BOOST_ICON ,
- BOOST_THUMBNAIL ,
- BOOST_UI ,
- BOOST_PREVIEW ,
- BOOST_MAP ,
- BOOST_MAP_VISIBLE ,
- BOOST_MAX_LEVEL,
-
- //other texture Categories
- LOCAL = BOOST_MAX_LEVEL,
- AVATAR_SCRATCH_TEX,
- DYNAMIC_TEX,
- MEDIA,
- ATLAS,
- OTHER,
- MAX_GL_IMAGE_CATEGORY
- };
-
- typedef enum
- {
- DELETED = 0, //removed from memory
- DELETION_CANDIDATE, //ready to be removed from memory
- INACTIVE, //not be used for the last certain period (i.e., 30 seconds).
- ACTIVE, //just being used, can become inactive if not being used for a certain time (10 seconds).
- NO_DELETE = 99 //stay in memory, can not be removed.
- } LLGLTextureState;
-
-protected:
- virtual ~LLGLTexture();
- LOG_CLASS(LLGLTexture);
-
-public:
- LLGLTexture(bool usemipmaps = true);
- LLGLTexture(const LLImageRaw* raw, bool usemipmaps) ;
- LLGLTexture(const U32 width, const U32 height, const U8 components, bool usemipmaps) ;
-
- virtual void dump(); // debug info to LL_INFOS()
-
- virtual const LLUUID& getID() const;
-
- void setBoostLevel(S32 level);
- S32 getBoostLevel() { return mBoostLevel; }
-
- S32 getFullWidth() const { return mFullWidth; }
- S32 getFullHeight() const { return mFullHeight; }
-
- void generateGLTexture() ;
- void destroyGLTexture() ;
-
- //---------------------------------------------------------------------------------------------
- //functions to access LLImageGL
- //---------------------------------------------------------------------------------------------
- /*virtual*/S32 getWidth(S32 discard_level = -1) const;
- /*virtual*/S32 getHeight(S32 discard_level = -1) const;
-
- bool hasGLTexture() const ;
- LLGLuint getTexName() const ;
- bool createGLTexture() ;
-
- // Create a GL Texture from an image raw
- // discard_level - mip level, 0 for highest resultion mip
- // imageraw - the image to copy from
- // usename - explicit GL name override
- // to_create - set to false to force gl texture to not be created
- // category - LLGLTexture category for this LLGLTexture
- // defer_copy - set to true to allocate GL texture but NOT initialize with imageraw data
- // tex_name - if not null, will be set to the GL name of the texture created
- bool createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, bool to_create = true, S32 category = LLGLTexture::OTHER, bool defer_copy = false, LLGLuint* tex_name = nullptr);
-
- void setFilteringOption(LLTexUnit::eTextureFilterOptions option);
- void setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format = 0, bool swap_bytes = false);
- void setAddressMode(LLTexUnit::eTextureAddressMode mode);
- bool setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, LLGLuint use_name = 0);
- bool setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, LLGLuint use_name = 0);
- void setGLTextureCreated (bool initialized);
- void setCategory(S32 category) ;
- void setTexName(LLGLuint); // for forcing w/ externally created textures only
- void setTarget(const LLGLenum target, const LLTexUnit::eTextureType bind_target);
-
- LLTexUnit::eTextureAddressMode getAddressMode(void) const ;
- S32 getMaxDiscardLevel() const;
- S32 getDiscardLevel() const;
- S8 getComponents() const;
- bool getBoundRecently() const;
- S32Bytes getTextureMemory() const ;
- LLGLenum getPrimaryFormat() const;
- bool getIsAlphaMask() const ;
- LLTexUnit::eTextureType getTarget(void) const ;
- bool getMask(const LLVector2 &tc);
- F32 getTimePassedSinceLastBound();
- bool getMissed() const ;
- bool isJustBound()const ;
- void forceUpdateBindStats(void) const;
-
- U32 getTexelsInAtlas() const ;
- U32 getTexelsInGLTexture() const ;
- bool isGLTextureCreated() const ;
- S32 getDiscardLevelInAtlas() const ;
- LLGLTextureState getTextureState() const { return mTextureState; }
-
- //---------------------------------------------------------------------------------------------
- //end of functions to access LLImageGL
- //---------------------------------------------------------------------------------------------
-
- //-----------------
- /*virtual*/ void setActive() ;
- void forceActive() ;
- void setNoDelete() ;
- void dontDiscard() { mDontDiscard = 1; mTextureState = NO_DELETE; }
- bool getDontDiscard() const { return mDontDiscard; }
- //-----------------
-
-private:
- void cleanup();
- void init();
-
-protected:
- void setTexelsPerImage();
-
-public:
- /*virtual*/ LLImageGL* getGLTexture() const ;
-
-protected:
- S32 mBoostLevel; // enum describing priority level
- U32 mFullWidth;
- U32 mFullHeight;
- bool mUseMipMaps;
- S8 mComponents;
- U32 mTexelsPerImage; // Texels per image.
- mutable S8 mNeedsGLTexture;
-
- //GL texture
- LLPointer<LLImageGL> mGLTexturep ;
- S8 mDontDiscard; // Keep full res version of this image (for UI, etc)
-
-protected:
- LLGLTextureState mTextureState ;
-
-
-};
-
-#endif // LL_GL_TEXTURE_H
-
+/**
+ * @file llgltexture.h
+ * @brief Object for managing opengl textures
+ *
+ * $LicenseInfo:firstyear=2012&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_GL_TEXTURE_H
+#define LL_GL_TEXTURE_H
+
+#include "lltexture.h"
+#include "llgl.h"
+
+class LLImageRaw;
+
+//
+//this the parent for the class LLViewerTexture
+//through the following virtual functions, the class LLViewerTexture can be reached from /llrender.
+//
+class LLGLTexture : public LLTexture
+{
+public:
+ enum
+ {
+ MAX_IMAGE_SIZE_DEFAULT = 1024,
+ INVALID_DISCARD_LEVEL = 0x7fff
+ };
+
+ enum EBoostLevel
+ {
+ BOOST_NONE = 0,
+ BOOST_AVATAR ,
+ BOOST_AVATAR_BAKED ,
+ BOOST_SCULPTED ,
+
+ BOOST_HIGH = 10,
+ BOOST_BUMP ,
+ BOOST_TERRAIN , // has to be high priority for minimap / low detail
+ BOOST_SELECTED ,
+ BOOST_AVATAR_BAKED_SELF ,
+ BOOST_AVATAR_SELF , // needed for baking avatar
+ BOOST_SUPER_HIGH , //textures higher than this need to be downloaded at the required resolution without delay.
+ BOOST_HUD ,
+ BOOST_ICON ,
+ BOOST_THUMBNAIL ,
+ BOOST_UI ,
+ BOOST_PREVIEW ,
+ BOOST_MAP ,
+ BOOST_MAP_VISIBLE ,
+ BOOST_MAX_LEVEL,
+
+ //other texture Categories
+ LOCAL = BOOST_MAX_LEVEL,
+ AVATAR_SCRATCH_TEX,
+ DYNAMIC_TEX,
+ MEDIA,
+ ATLAS,
+ OTHER,
+ MAX_GL_IMAGE_CATEGORY
+ };
+
+ typedef enum
+ {
+ DELETED = 0, //removed from memory
+ DELETION_CANDIDATE, //ready to be removed from memory
+ INACTIVE, //not be used for the last certain period (i.e., 30 seconds).
+ ACTIVE, //just being used, can become inactive if not being used for a certain time (10 seconds).
+ NO_DELETE = 99 //stay in memory, can not be removed.
+ } LLGLTextureState;
+
+protected:
+ virtual ~LLGLTexture();
+ LOG_CLASS(LLGLTexture);
+
+public:
+ LLGLTexture(bool usemipmaps = true);
+ LLGLTexture(const LLImageRaw* raw, bool usemipmaps) ;
+ LLGLTexture(const U32 width, const U32 height, const U8 components, bool usemipmaps) ;
+
+ virtual void dump(); // debug info to LL_INFOS()
+
+ virtual const LLUUID& getID() const;
+
+ void setBoostLevel(S32 level);
+ S32 getBoostLevel() { return mBoostLevel; }
+
+ S32 getFullWidth() const { return mFullWidth; }
+ S32 getFullHeight() const { return mFullHeight; }
+
+ void generateGLTexture() ;
+ void destroyGLTexture() ;
+
+ //---------------------------------------------------------------------------------------------
+ //functions to access LLImageGL
+ //---------------------------------------------------------------------------------------------
+ /*virtual*/S32 getWidth(S32 discard_level = -1) const;
+ /*virtual*/S32 getHeight(S32 discard_level = -1) const;
+
+ bool hasGLTexture() const ;
+ LLGLuint getTexName() const ;
+ bool createGLTexture() ;
+
+ // Create a GL Texture from an image raw
+ // discard_level - mip level, 0 for highest resultion mip
+ // imageraw - the image to copy from
+ // usename - explicit GL name override
+ // to_create - set to false to force gl texture to not be created
+ // category - LLGLTexture category for this LLGLTexture
+ // defer_copy - set to true to allocate GL texture but NOT initialize with imageraw data
+ // tex_name - if not null, will be set to the GL name of the texture created
+ bool createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, bool to_create = true, S32 category = LLGLTexture::OTHER, bool defer_copy = false, LLGLuint* tex_name = nullptr);
+
+ void setFilteringOption(LLTexUnit::eTextureFilterOptions option);
+ void setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format = 0, bool swap_bytes = false);
+ void setAddressMode(LLTexUnit::eTextureAddressMode mode);
+ bool setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, LLGLuint use_name = 0);
+ bool setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, LLGLuint use_name = 0);
+ void setGLTextureCreated (bool initialized);
+ void setCategory(S32 category) ;
+ void setTexName(LLGLuint); // for forcing w/ externally created textures only
+ void setTarget(const LLGLenum target, const LLTexUnit::eTextureType bind_target);
+
+ LLTexUnit::eTextureAddressMode getAddressMode(void) const ;
+ S32 getMaxDiscardLevel() const;
+ S32 getDiscardLevel() const;
+ S8 getComponents() const;
+ bool getBoundRecently() const;
+ S32Bytes getTextureMemory() const ;
+ LLGLenum getPrimaryFormat() const;
+ bool getIsAlphaMask() const ;
+ LLTexUnit::eTextureType getTarget(void) const ;
+ bool getMask(const LLVector2 &tc);
+ F32 getTimePassedSinceLastBound();
+ bool getMissed() const ;
+ bool isJustBound()const ;
+ void forceUpdateBindStats(void) const;
+
+ U32 getTexelsInAtlas() const ;
+ U32 getTexelsInGLTexture() const ;
+ bool isGLTextureCreated() const ;
+ S32 getDiscardLevelInAtlas() const ;
+ LLGLTextureState getTextureState() const { return mTextureState; }
+
+ //---------------------------------------------------------------------------------------------
+ //end of functions to access LLImageGL
+ //---------------------------------------------------------------------------------------------
+
+ //-----------------
+ /*virtual*/ void setActive() ;
+ void forceActive() ;
+ void setNoDelete() ;
+ void dontDiscard() { mDontDiscard = 1; mTextureState = NO_DELETE; }
+ bool getDontDiscard() const { return mDontDiscard; }
+ //-----------------
+
+private:
+ void cleanup();
+ void init();
+
+protected:
+ void setTexelsPerImage();
+
+public:
+ /*virtual*/ LLImageGL* getGLTexture() const ;
+
+protected:
+ S32 mBoostLevel; // enum describing priority level
+ U32 mFullWidth;
+ U32 mFullHeight;
+ bool mUseMipMaps;
+ S8 mComponents;
+ U32 mTexelsPerImage; // Texels per image.
+ mutable S8 mNeedsGLTexture;
+
+ //GL texture
+ LLPointer<LLImageGL> mGLTexturep ;
+ S8 mDontDiscard; // Keep full res version of this image (for UI, etc)
+
+protected:
+ LLGLTextureState mTextureState ;
+
+
+};
+
+#endif // LL_GL_TEXTURE_H
+
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index c4170ffacb..59f3dd56d6 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -1,2547 +1,2547 @@
-/**
- * @file llimagegl.cpp
- * @brief Generic GL image handler
- *
- * $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$
- */
-
-
-// TODO: create 2 classes for images w/ and w/o discard levels?
-
-#include "linden_common.h"
-
-#include "llimagegl.h"
-
-#include "llerror.h"
-#include "llfasttimer.h"
-#include "llimage.h"
-
-#include "llmath.h"
-#include "llgl.h"
-#include "llglslshader.h"
-#include "llrender.h"
-#include "llwindow.h"
-#include "llframetimer.h"
-
-extern LL_COMMON_API bool on_main_thread();
-
-#if !LL_IMAGEGL_THREAD_CHECK
-#define checkActiveThread()
-#endif
-
-//----------------------------------------------------------------------------
-const F32 MIN_TEXTURE_LIFETIME = 10.f;
-
-//which power of 2 is i?
-//assumes i is a power of 2 > 0
-U32 wpo2(U32 i);
-
-
-// texture memory accounting (for macOS)
-static LLMutex sTexMemMutex;
-static std::unordered_map<U32, U64> sTextureAllocs;
-static U64 sTextureBytes = 0;
-
-// track a texture alloc on the currently bound texture.
-// asserts that no currently tracked alloc exists
-void LLImageGLMemory::alloc_tex_image(U32 width, U32 height, U32 pixformat)
-{
- U32 texUnit = gGL.getCurrentTexUnitIndex();
- U32 texName = gGL.getTexUnit(texUnit)->getCurrTexture();
- U64 size = LLImageGL::dataFormatBytes(pixformat, width, height);
-
- llassert(size >= 0);
-
- sTexMemMutex.lock();
- llassert(sTextureAllocs.find(texName) == sTextureAllocs.end());
-
- sTextureAllocs[texName] = size;
- sTextureBytes += size;
-
- sTexMemMutex.unlock();
-}
-
-// track texture free on given texName
-void LLImageGLMemory::free_tex_image(U32 texName)
-{
- sTexMemMutex.lock();
- auto iter = sTextureAllocs.find(texName);
- if (iter != sTextureAllocs.end())
- {
- llassert(iter->second <= sTextureBytes); // sTextureBytes MUST NOT go below zero
-
- sTextureBytes -= iter->second;
-
- sTextureAllocs.erase(iter);
- }
-
- sTexMemMutex.unlock();
-}
-
-// track texture free on given texNames
-void LLImageGLMemory::free_tex_images(U32 count, const U32* texNames)
-{
- for (int i = 0; i < count; ++i)
- {
- free_tex_image(texNames[i]);
- }
-}
-
-// track texture free on currently bound texture
-void LLImageGLMemory::free_cur_tex_image()
-{
- U32 texUnit = gGL.getCurrentTexUnitIndex();
- U32 texName = gGL.getTexUnit(texUnit)->getCurrTexture();
- free_tex_image(texName);
-}
-
-using namespace LLImageGLMemory;
-
-// static
-U64 LLImageGL::getTextureBytesAllocated()
-{
- return sTextureBytes;
-}
-
-//statics
-
-U32 LLImageGL::sUniqueCount = 0;
-U32 LLImageGL::sBindCount = 0;
-S32 LLImageGL::sCount = 0;
-
-bool LLImageGL::sGlobalUseAnisotropic = false;
-F32 LLImageGL::sLastFrameTime = 0.f;
-bool LLImageGL::sAllowReadBackRaw = false ;
-LLImageGL* LLImageGL::sDefaultGLTexture = NULL ;
-bool LLImageGL::sCompressTextures = false;
-std::set<LLImageGL*> LLImageGL::sImageList;
-
-
-bool LLImageGLThread::sEnabledTextures = false;
-bool LLImageGLThread::sEnabledMedia = false;
-
-//****************************************************************************************************
-//The below for texture auditing use only
-//****************************************************************************************************
-//-----------------------
-//debug use
-S32 LLImageGL::sCurTexSizeBar = -1 ;
-S32 LLImageGL::sCurTexPickSize = -1 ;
-S32 LLImageGL::sMaxCategories = 1 ;
-
-//optimization for when we don't need to calculate mIsMask
-bool LLImageGL::sSkipAnalyzeAlpha;
-
-//------------------------
-//****************************************************************************************************
-//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)
- {
- LL_INFOS() << "Bound: " << texname << " Should bind: " << mTexName << " Default: " << LLImageGL::sDefaultGLTexture->getTexName() << LL_ENDL;
-
- error = true;
- if (gDebugSession)
- {
- gFailLog << "Invalid texture bound!" << std::endl;
- }
- else
- {
- LL_ERRS() << "Invalid texture bound!" << LL_ENDL;
- }
- }
- 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
- {
- LL_ERRS() << "wrong texture size and discard level: width: " <<
- mWidth << " Height: " << mHeight << " Current Level: " << (S32)mCurrentDiscardLevel << LL_ENDL ;
- }
- }
-
- 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(LLWindow* window, S32 num_catagories, bool skip_analyze_alpha /* = false */, bool thread_texture_loads /* = false */, bool thread_media_updates /* = false */)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- sSkipAnalyzeAlpha = skip_analyze_alpha;
-
- if (thread_texture_loads || thread_media_updates)
- {
- LLImageGLThread::createInstance(window);
- LLImageGLThread::sEnabledTextures = thread_texture_loads;
- LLImageGLThread::sEnabledMedia = thread_media_updates;
- }
-}
-
-//static
-void LLImageGL::cleanupClass()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- LLImageGLThread::deleteSingleton();
-}
-
-
-//static
-S32 LLImageGL::dataFormatBits(S32 dataformat)
-{
- switch (dataformat)
- {
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return 4;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return 4;
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return 8;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return 8;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return 8;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return 8;
- case GL_LUMINANCE: return 8;
- case GL_ALPHA: return 8;
- case GL_RED: return 8;
- case GL_COLOR_INDEX: return 8;
- case GL_LUMINANCE_ALPHA: return 16;
- case GL_RGB: return 24;
- case GL_SRGB: return 24;
- case GL_RGB8: return 24;
- case GL_RGBA: return 32;
- case GL_SRGB_ALPHA: return 32;
- case GL_BGRA: return 32; // Used for QuickTime media textures on the Mac
- case GL_DEPTH_COMPONENT: return 24;
- case GL_RGB16F: return 48;
- case GL_RGBA16F: return 64;
- default:
- LL_ERRS() << "LLImageGL::Unknown format: " << dataformat << LL_ENDL;
- return 0;
- }
-}
-
-//static
-S64 LLImageGL::dataFormatBytes(S32 dataformat, S32 width, S32 height)
-{
- switch (dataformat)
- {
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
- if (width < 4) width = 4;
- if (height < 4) height = 4;
- break;
- default:
- break;
- }
- S64 bytes (((S64)width * (S64)height * (S64)dataFormatBits(dataformat)+7)>>3);
- S64 aligned = (bytes+3)&~3;
- return aligned;
-}
-
-//static
-S32 LLImageGL::dataFormatComponents(S32 dataformat)
-{
- switch (dataformat)
- {
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return 3;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return 3;
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return 4;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return 4;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return 4;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return 4;
- case GL_LUMINANCE: return 1;
- case GL_ALPHA: return 1;
- case GL_RED: return 1;
- case GL_COLOR_INDEX: return 1;
- case GL_LUMINANCE_ALPHA: return 2;
- case GL_RGB: return 3;
- case GL_SRGB: return 3;
- case GL_RGBA: return 4;
- case GL_SRGB_ALPHA: return 4;
- case GL_BGRA: return 4; // Used for QuickTime media textures on the Mac
- default:
- LL_ERRS() << "LLImageGL::Unknown format: " << dataformat << LL_ENDL;
- return 0;
- }
-}
-
-//----------------------------------------------------------------------------
-
-// static
-void LLImageGL::updateStats(F32 current_time)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- sLastFrameTime = current_time;
-}
-
-//----------------------------------------------------------------------------
-
-//static
-void LLImageGL::destroyGL(bool save_state)
-{
- for (S32 stage = 0; stage < gGLManager.mNumTextureImageUnits; stage++)
- {
- 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)
- {
- if (save_state && glimage->isGLTextureCreated() && glimage->mComponents)
- {
- glimage->mSaveData = new LLImageRaw;
- if(!glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData, false)) //necessary, keep it.
- {
- glimage->mSaveData = NULL ;
- }
- }
-
- glimage->destroyGLTexture();
- stop_glerror();
- }
- }
- sAllowReadBackRaw = false ;
-}
-
-//static
-void LLImageGL::restoreGL()
-{
- for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
- iter != sImageList.end(); iter++)
- {
- LLImageGL* glimage = *iter;
- if(glimage->getTexName())
- {
- LL_ERRS() << "tex name is not 0." << LL_ENDL ;
- }
- if (glimage->mSaveData.notNull())
- {
- if (glimage->getComponents() && glimage->mSaveData->getComponents())
- {
- glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData, 0, true, glimage->getCategory());
- stop_glerror();
- }
- glimage->mSaveData = NULL; // deletes data
- }
- }
-}
-
-//static
-void LLImageGL::dirtyTexOptions()
-{
- for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
- iter != sImageList.end(); iter++)
- {
- LLImageGL* glimage = *iter;
- glimage->mTexOptionsDirty = true;
- stop_glerror();
- }
-
-}
-//----------------------------------------------------------------------------
-
-//for server side use only.
-//static
-bool LLImageGL::create(LLPointer<LLImageGL>& dest, bool usemipmaps)
-{
- dest = new LLImageGL(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);
- return true;
-}
-
-//----------------------------------------------------------------------------
-
-LLImageGL::LLImageGL(bool usemipmaps)
-: mSaveData(0), mExternalTexture(false)
-{
- init(usemipmaps);
- setSize(0, 0, 0);
- sImageList.insert(this);
- sCount++;
-}
-
-LLImageGL::LLImageGL(U32 width, U32 height, U8 components, bool usemipmaps)
-: mSaveData(0), mExternalTexture(false)
-{
- llassert( components <= 4 );
- init(usemipmaps);
- setSize(width, height, components);
- sImageList.insert(this);
- sCount++;
-}
-
-LLImageGL::LLImageGL(const LLImageRaw* imageraw, bool usemipmaps)
-: mSaveData(0), mExternalTexture(false)
-{
- init(usemipmaps);
- setSize(0, 0, 0);
- sImageList.insert(this);
- sCount++;
-
- createGLTexture(0, imageraw);
-}
-
-LLImageGL::LLImageGL(
- LLGLuint texName,
- U32 components,
- LLGLenum target,
- LLGLint formatInternal,
- LLGLenum formatPrimary,
- LLGLenum formatType,
- LLTexUnit::eTextureAddressMode addressMode)
-{
- init(false);
- mTexName = texName;
- mTarget = target;
- mComponents = components;
- mAddressMode = addressMode;
- mFormatType = formatType;
- mFormatInternal = formatInternal;
- mFormatPrimary = formatPrimary;
-}
-
-
-LLImageGL::~LLImageGL()
-{
- if (!mExternalTexture && gGLManager.mInited)
- {
- LLImageGL::cleanup();
- sImageList.erase(this);
- freePickMask();
- sCount--;
- }
-}
-
-void LLImageGL::init(bool usemipmaps)
-{
-#if LL_IMAGEGL_THREAD_CHECK
- mActiveThread = LLThread::currentID();
-#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 = S64Bytes(0);
- mLastBindTime = 0.f;
-
- mPickMask = NULL;
- mPickMaskWidth = 0;
- mPickMaskHeight = 0;
- mUseMipMaps = usemipmaps;
- mHasExplicitFormat = 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 ;
-
- mAllowCompression = true;
-
- mTarget = GL_TEXTURE_2D;
- mBindTarget = LLTexUnit::TT_TEXTURE;
- mHasMipMaps = false;
- mMipLevels = -1;
-
- mIsResident = 0;
-
- mComponents = 0;
- mMaxDiscardLevel = MAX_DISCARD_LEVEL;
-
- mTexOptionsDirty = true;
- mAddressMode = LLTexUnit::TAM_WRAP;
- mFilterOption = LLTexUnit::TFO_ANISOTROPIC;
-
- mFormatInternal = -1;
- mFormatPrimary = (LLGLenum) 0;
- mFormatType = GL_UNSIGNED_BYTE;
- mFormatSwapBytes = false;
-
-#ifdef DEBUG_MISS
- mMissed = false;
-#endif
-
- mCategory = -1;
-
- // Sometimes we have to post work for the main thread.
- mMainQueue = LL::WorkQueue::getInstance("mainloop");
-}
-
-void LLImageGL::cleanup()
-{
- if (!gGLManager.mIsDisabled)
- {
- destroyGLTexture();
- }
- freePickMask();
-
- mSaveData = NULL; // deletes data
-}
-
-//----------------------------------------------------------------------------
-
-//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)
-{
- if(dim < 0)
- {
- return false ;
- }
- if(!dim)//0 is a power-of-two number
- {
- return true ;
- }
- return !(dim & (dim - 1)) ;
-}
-
-//static
-bool LLImageGL::checkSize(S32 width, S32 height)
-{
- return check_power_of_two(width) && check_power_of_two(height);
-}
-
-bool LLImageGL::setSize(S32 width, S32 height, S32 ncomponents, S32 discard_level)
-{
- if (width != mWidth || height != mHeight || ncomponents != mComponents)
- {
- // Check if dimensions are a power of two!
- if (!checkSize(width, height))
- {
- LL_WARNS() << llformat("Texture has non power of two dimension: %dx%d",width,height) << LL_ENDL;
- return false;
- }
-
- // pickmask validity depends on old image size, delete it
- freePickMask();
-
- mWidth = width;
- mHeight = height;
- mComponents = ncomponents;
- if (ncomponents > 0)
- {
- mMaxDiscardLevel = 0;
- while (width > 1 && height > 1 && mMaxDiscardLevel < MAX_DISCARD_LEVEL)
- {
- mMaxDiscardLevel++;
- width >>= 1;
- height >>= 1;
- }
-
- if(discard_level > 0)
- {
- mMaxDiscardLevel = llmax(mMaxDiscardLevel, (S8)discard_level);
- }
- }
- else
- {
- mMaxDiscardLevel = MAX_DISCARD_LEVEL;
- }
- }
-
- return true;
-}
-
-//----------------------------------------------------------------------------
-
-// virtual
-void LLImageGL::dump()
-{
- LL_INFOS() << "mMaxDiscardLevel " << S32(mMaxDiscardLevel)
- << " mLastBindTime " << mLastBindTime
- << " mTarget " << S32(mTarget)
- << " mBindTarget " << S32(mBindTarget)
- << " mUseMipMaps " << S32(mUseMipMaps)
- << " mHasMipMaps " << S32(mHasMipMaps)
- << " mCurrentDiscardLevel " << S32(mCurrentDiscardLevel)
- << " mFormatInternal " << S32(mFormatInternal)
- << " mFormatPrimary " << S32(mFormatPrimary)
- << " mFormatType " << S32(mFormatType)
- << " mFormatSwapBytes " << S32(mFormatSwapBytes)
- << " mHasExplicitFormat " << S32(mHasExplicitFormat)
-#if DEBUG_MISS
- << " mMissed " << mMissed
-#endif
- << LL_ENDL;
-
- LL_INFOS() << " mTextureMemory " << mTextureMemory
- << " mTexNames " << mTexName
- << " mIsResident " << S32(mIsResident)
- << LL_ENDL;
-}
-
-//----------------------------------------------------------------------------
-void LLImageGL::forceUpdateBindStats(void) const
-{
- mLastBindTime = sLastFrameTime;
-}
-
-bool LLImageGL::updateBindStats() const
-{
- if (mTexName != 0)
- {
-#ifdef DEBUG_MISS
- mMissed = ! getIsResident(true);
-#endif
- sBindCount++;
- if (mLastBindTime != sLastFrameTime)
- {
- // we haven't accounted for this texture yet this frame
- sUniqueCount++;
- mLastBindTime = sLastFrameTime;
-
- return true ;
- }
- }
- return false ;
-}
-
-F32 LLImageGL::getTimePassedSinceLastBound()
-{
- return sLastFrameTime - mLastBindTime ;
-}
-
-void LLImageGL::setExplicitFormat( LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, bool swap_bytes )
-{
- // Note: must be called before createTexture()
- // Note: it's up to the caller to ensure that the format matches the number of components.
- mHasExplicitFormat = true;
- mFormatInternal = internal_format;
- mFormatPrimary = primary_format;
- if(type_format == 0)
- mFormatType = GL_UNSIGNED_BYTE;
- else
- mFormatType = type_format;
- mFormatSwapBytes = swap_bytes;
-
- calcAlphaChannelOffsetAndStride() ;
-}
-
-//----------------------------------------------------------------------------
-
-void LLImageGL::setImage(const LLImageRaw* imageraw)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- llassert((imageraw->getWidth() == getWidth(mCurrentDiscardLevel)) &&
- (imageraw->getHeight() == getHeight(mCurrentDiscardLevel)) &&
- (imageraw->getComponents() == getComponents()));
- const U8* rawdata = imageraw->getData();
- setImage(rawdata, false);
-}
-
-bool LLImageGL::setImage(const U8* data_in, bool data_hasmips /* = false */, S32 usename /* = 0 */)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
-
- const bool is_compressed = isCompressed();
-
- if (mUseMipMaps)
- {
- //set has mip maps to true before binding image so tex parameters get set properly
- gGL.getTexUnit(0)->unbind(mBindTarget);
-
- mHasMipMaps = true;
- mTexOptionsDirty = true;
- setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
- }
- else
- {
- mHasMipMaps = false;
- }
-
- gGL.getTexUnit(0)->bind(this, false, false, usename);
-
- if (data_in == nullptr)
- {
- S32 w = getWidth();
- S32 h = getHeight();
- LLImageGL::setManualImage(mTarget, 0, mFormatInternal, w, h,
- mFormatPrimary, mFormatType, (GLvoid*)data_in, mAllowCompression);
- }
- else if (mUseMipMaps)
- {
- 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;
-
- mMipLevels = llmax(mMipLevels, gl_level);
-
- if (d > mCurrentDiscardLevel)
- {
- data_in -= dataFormatBytes(mFormatPrimary, w, h); // see above comment
- }
- if (is_compressed)
- {
- S32 tex_size = dataFormatBytes(mFormatPrimary, w, h);
- glCompressedTexImage2D(mTarget, gl_level, mFormatPrimary, w, h, 0, tex_size, (GLvoid *)data_in);
- stop_glerror();
- }
- else
- {
- if(mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
- stop_glerror();
- }
-
- LLImageGL::setManualImage(mTarget, gl_level, mFormatInternal, w, h, mFormatPrimary, GL_UNSIGNED_BYTE, (GLvoid*)data_in, mAllowCompression);
- if (gl_level == 0)
- {
- analyzeAlpha(data_in, w, h);
- }
- updatePickMask(w, h, data_in);
-
- if(mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
- stop_glerror();
- }
-
- stop_glerror();
- }
- stop_glerror();
- }
- }
- else if (!is_compressed)
- {
- if (mAutoGenMips)
- {
- stop_glerror();
- {
- if(mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
- stop_glerror();
- }
-
- S32 w = getWidth(mCurrentDiscardLevel);
- S32 h = getHeight(mCurrentDiscardLevel);
-
- mMipLevels = wpo2(llmax(w, h));
-
- //use legacy mipmap generation mode (note: making this condional can cause rendering issues)
- // -- but making it not conditional triggers deprecation warnings when core profile is enabled
- // (some rendering issues while core profile is enabled are acceptable at this point in time)
- if (!LLRender::sGLCoreProfile)
- {
- glTexParameteri(mTarget, GL_GENERATE_MIPMAP, GL_TRUE);
- }
-
- LLImageGL::setManualImage(mTarget, 0, mFormatInternal,
- w, h,
- mFormatPrimary, mFormatType,
- data_in, mAllowCompression);
- analyzeAlpha(data_in, w, h);
- stop_glerror();
-
- updatePickMask(w, h, data_in);
-
- if(mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
- stop_glerror();
- }
-
- if (LLRender::sGLCoreProfile)
- {
- LL_PROFILE_GPU_ZONE("generate mip map");
- glGenerateMipmap(mTarget);
- }
- stop_glerror();
- }
- }
- else
- {
- // Create mips by hand
- // ~4x faster than gluBuild2DMipmaps
- S32 width = getWidth(mCurrentDiscardLevel);
- S32 height = getHeight(mCurrentDiscardLevel);
- S32 nummips = mMaxDiscardLevel - mCurrentDiscardLevel + 1;
- S32 w = width, h = height;
-
-
- const U8* new_data = 0;
- (void)new_data;
-
- const U8* prev_mip_data = 0;
- const U8* cur_mip_data = 0;
-#ifdef SHOW_ASSERT
- S32 cur_mip_size = 0;
-#endif
- mMipLevels = nummips;
-
- for (int m=0; m<nummips; m++)
- {
- if (m==0)
- {
- cur_mip_data = data_in;
-#ifdef SHOW_ASSERT
- cur_mip_size = width * height * mComponents;
-#endif
- }
- else
- {
- S32 bytes = w * h * mComponents;
-#ifdef SHOW_ASSERT
- llassert(prev_mip_data);
- llassert(cur_mip_size == bytes*4);
-#endif
- U8* new_data = new(std::nothrow) U8[bytes];
- if (!new_data)
- {
- stop_glerror();
-
- if (prev_mip_data)
- {
- if (prev_mip_data != cur_mip_data)
- delete[] prev_mip_data;
- prev_mip_data = nullptr;
- }
- if (cur_mip_data)
- {
- delete[] cur_mip_data;
- cur_mip_data = nullptr;
- }
-
- mGLTextureCreated = false;
- return false;
- }
- else
- {
-
-#ifdef SHOW_ASSERT
- llassert(prev_mip_data);
- llassert(cur_mip_size == bytes * 4);
-#endif
-
- LLImageBase::generateMip(prev_mip_data, new_data, w, h, mComponents);
- cur_mip_data = new_data;
-#ifdef SHOW_ASSERT
- cur_mip_size = bytes;
-#endif
- }
-
- }
- llassert(w > 0 && h > 0 && cur_mip_data);
- (void)cur_mip_data;
- {
- if(mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
- stop_glerror();
- }
-
- LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data, mAllowCompression);
- if (m == 0)
- {
- analyzeAlpha(data_in, w, h);
- }
- stop_glerror();
- if (m == 0)
- {
- updatePickMask(w, h, cur_mip_data);
- }
-
- if(mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
- stop_glerror();
- }
- }
- if (prev_mip_data && prev_mip_data != data_in)
- {
- delete[] prev_mip_data;
- }
- prev_mip_data = cur_mip_data;
- w >>= 1;
- h >>= 1;
- }
- if (prev_mip_data && prev_mip_data != data_in)
- {
- delete[] prev_mip_data;
- prev_mip_data = NULL;
- }
- }
- }
- else
- {
- LL_ERRS() << "Compressed Image has mipmaps but data does not (can not auto generate compressed mips)" << LL_ENDL;
- }
- }
- else
- {
- mMipLevels = 0;
- S32 w = getWidth();
- S32 h = getHeight();
- if (is_compressed)
- {
- S32 tex_size = dataFormatBytes(mFormatPrimary, w, h);
- glCompressedTexImage2D(mTarget, 0, mFormatPrimary, w, h, 0, tex_size, (GLvoid *)data_in);
- stop_glerror();
- }
- else
- {
- if(mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
- stop_glerror();
- }
-
- LLImageGL::setManualImage(mTarget, 0, mFormatInternal, w, h,
- mFormatPrimary, mFormatType, (GLvoid *)data_in, mAllowCompression);
- analyzeAlpha(data_in, w, h);
-
- updatePickMask(w, h, data_in);
-
- stop_glerror();
-
- if(mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
- stop_glerror();
- }
-
- }
- }
- stop_glerror();
- mGLTextureCreated = true;
- return true;
-}
-
-bool LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image)
-{
- //not compatible with core GL profile
- llassert(!LLRender::sGLCoreProfile);
-
- if (gGLManager.mIsDisabled)
- {
- LL_WARNS() << "Trying to create a texture while GL is disabled!" << LL_ENDL;
- return false;
- }
- llassert(gGLManager.mInited);
- stop_glerror();
-
- if (discard_level < 0)
- {
- llassert(mCurrentDiscardLevel >= 0);
- discard_level = mCurrentDiscardLevel;
- }
-
- // 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
- if (!setSize(w, h, raw_image->getComponents(), discard_level))
- {
- LL_WARNS() << "Trying to create a texture with incorrect dimensions!" << LL_ENDL;
- return false;
- }
-
- 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:
- LL_ERRS() << "Bad number of components for texture: " << (U32) getComponents() << LL_ENDL;
- }
- }
-
- 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 ;
-}
-
-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();
-}
-
-U32 type_width_from_pixtype(U32 pixtype)
-{
- U32 type_width = 0;
- switch (pixtype)
- {
- case GL_UNSIGNED_BYTE:
- case GL_BYTE:
- case GL_UNSIGNED_INT_8_8_8_8_REV:
- type_width = 1;
- break;
- case GL_UNSIGNED_SHORT:
- case GL_SHORT:
- type_width = 2;
- break;
- case GL_UNSIGNED_INT:
- case GL_INT:
- case GL_FLOAT:
- type_width = 4;
- break;
- default:
- LL_ERRS() << "Unknown type: " << pixtype << LL_ENDL;
- }
- return type_width;
-}
-
-bool should_stagger_image_set(bool compressed)
-{
-#if LL_DARWIN
- return false;
-#else
- // glTexSubImage2D doesn't work with compressed textures on select tested Nvidia GPUs on Windows 10 -Cosmic,2023-03-08
- // Setting media textures off-thread seems faster when not using sub_image_lines (Nvidia/Windows 10) -Cosmic,2023-03-31
- return !compressed && on_main_thread();
-#endif
-}
-
-// Equivalent to calling glSetSubImage2D(target, miplevel, x_offset, y_offset, width, height, pixformat, pixtype, src), assuming the total width of the image is data_width
-// However, instead there are multiple calls to glSetSubImage2D on smaller slices of the image
-void sub_image_lines(U32 target, S32 miplevel, S32 x_offset, S32 y_offset, S32 width, S32 height, U32 pixformat, U32 pixtype, const U8* src, S32 data_width)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
-
- U32 components = LLImageGL::dataFormatComponents(pixformat);
- U32 type_width = type_width_from_pixtype(pixtype);
-
- const U32 line_width = data_width * components * type_width;
- const U32 y_offset_end = y_offset + height;
- for (U32 y_pos = y_offset; y_pos < y_offset_end; ++y_pos)
- {
- glTexSubImage2D(target, miplevel, x_offset, y_pos, width, 1, pixformat, pixtype, src);
- src += line_width;
- }
-}
-
-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 /* = false */, LLGLuint use_name)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- if (!width || !height)
- {
- return true;
- }
- LLGLuint tex_name = use_name != 0 ? use_name : mTexName;
- if (0 == tex_name)
- {
- // *TODO: Re-enable warning? Ran into thread locking issues? DK 2011-02-18
- //LL_WARNS() << "Setting subimage on image without GL texture" << LL_ENDL;
- return false;
- }
- if (datap == NULL)
- {
- // *TODO: Re-enable warning? Ran into thread locking issues? DK 2011-02-18
- //LL_WARNS() << "Setting subimage on image with NULL datap" << LL_ENDL;
- return false;
- }
-
- // 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, tex_name);
- }
- else
- {
- if (mUseMipMaps)
- {
- dump();
- LL_ERRS() << "setSubImage called with mipmapped image (not supported)" << LL_ENDL;
- }
- llassert_always(mCurrentDiscardLevel == 0);
- llassert_always(x_pos >= 0 && y_pos >= 0);
-
- if (((x_pos + width) > getWidth()) ||
- (y_pos + height) > getHeight())
- {
- dump();
- LL_ERRS() << "Subimage not wholly in target image!"
- << " x_pos " << x_pos
- << " y_pos " << y_pos
- << " width " << width
- << " height " << height
- << " getWidth() " << getWidth()
- << " getHeight() " << getHeight()
- << LL_ENDL;
- }
-
- if ((x_pos + width) > data_width ||
- (y_pos + height) > data_height)
- {
- dump();
- LL_ERRS() << "Subimage not wholly in source image!"
- << " x_pos " << x_pos
- << " y_pos " << y_pos
- << " width " << width
- << " height " << height
- << " source_width " << data_width
- << " source_height " << data_height
- << LL_ENDL;
- }
-
-
- glPixelStorei(GL_UNPACK_ROW_LENGTH, data_width);
- stop_glerror();
-
- if(mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
- stop_glerror();
- }
-
- const U8* sub_datap = datap + (y_pos * data_width + x_pos) * getComponents();
- // Update the GL texture
- bool res = gGL.getTexUnit(0)->bindManual(mBindTarget, tex_name);
- if (!res) LL_ERRS() << "LLImageGL::setSubImage(): bindTexture failed" << LL_ENDL;
- stop_glerror();
-
- const bool use_sub_image = should_stagger_image_set(isCompressed());
- if (!use_sub_image)
- {
- // *TODO: Why does this work here, in setSubImage, but not in
- // setManualImage? Maybe because it only gets called with the
- // dimensions of the full image? Or because the image is never
- // compressed?
- glTexSubImage2D(mTarget, 0, x_pos, y_pos, width, height, mFormatPrimary, mFormatType, sub_datap);
- }
- else
- {
- sub_image_lines(mTarget, 0, x_pos, y_pos, width, height, mFormatPrimary, mFormatType, sub_datap, data_width);
- }
- gGL.getTexUnit(0)->disable();
- stop_glerror();
-
- if(mFormatSwapBytes)
- {
- glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
- stop_glerror();
- }
-
- 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 force_fast_update /* = false */, LLGLuint use_name)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height, force_fast_update, use_name);
-}
-
-// 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 (gGL.getTexUnit(0)->bind(this, false, true))
- {
- glCopyTexSubImage2D(GL_TEXTURE_2D, 0, fb_x, fb_y, x_pos, y_pos, width, height);
- mGLTextureCreated = true;
- stop_glerror();
- return true;
- }
- else
- {
- return false;
- }
-}
-
-// static
-void LLImageGL::generateTextures(S32 numTextures, U32 *textures)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- static constexpr U32 pool_size = 1024;
- static thread_local U32 name_pool[pool_size]; // pool of texture names
- static thread_local U32 name_count = 0; // number of available names in the pool
-
- if (name_count == 0)
- {
- LL_PROFILE_ZONE_NAMED("iglgt - reup pool");
- // pool is emtpy, refill it
- glGenTextures(pool_size, name_pool);
- name_count = pool_size;
- }
-
- if (numTextures <= name_count)
- {
- //copy teture names off the end of the pool
- memcpy(textures, name_pool + name_count - numTextures, sizeof(U32) * numTextures);
- name_count -= numTextures;
- }
- else
- {
- LL_PROFILE_ZONE_NAMED("iglgt - pool miss");
- glGenTextures(numTextures, textures);
- }
-}
-
-// static
-void LLImageGL::deleteTextures(S32 numTextures, const U32 *textures)
-{
- if (gGLManager.mInited)
- {
- free_tex_images(numTextures, textures);
- glDeleteTextures(numTextures, textures);
- }
-}
-
-// static
-void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void* pixels, bool allow_compression)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- bool use_scratch = false;
- U32* scratch = NULL;
- if (LLRender::sGLCoreProfile)
- {
- if (pixformat == GL_ALPHA && pixtype == GL_UNSIGNED_BYTE)
- { //GL_ALPHA is deprecated, convert to RGBA
- if (pixels != nullptr)
- {
- use_scratch = true;
- scratch = new(std::nothrow) U32[width * height];
- if (!scratch)
- {
- LLError::LLUserWarningMsg::showOutOfMemory();
- LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
- << " bytes for a manual image W" << width << " H" << height << LL_ENDL;
- }
-
- U32 pixel_count = (U32)(width * height);
- for (U32 i = 0; i < pixel_count; i++)
- {
- U8* pix = (U8*)&scratch[i];
- pix[0] = pix[1] = pix[2] = 0;
- pix[3] = ((U8*)pixels)[i];
- }
- }
-
- pixformat = GL_RGBA;
- intformat = GL_RGBA8;
- }
-
- if (pixformat == GL_LUMINANCE_ALPHA && pixtype == GL_UNSIGNED_BYTE)
- { //GL_LUMINANCE_ALPHA is deprecated, convert to RGBA
- if (pixels != nullptr)
- {
- use_scratch = true;
- scratch = new(std::nothrow) U32[width * height];
- if (!scratch)
- {
- LLError::LLUserWarningMsg::showOutOfMemory();
- LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
- << " bytes for a manual image W" << width << " H" << height << LL_ENDL;
- }
-
- U32 pixel_count = (U32)(width * height);
- for (U32 i = 0; i < pixel_count; i++)
- {
- U8 lum = ((U8*)pixels)[i * 2 + 0];
- U8 alpha = ((U8*)pixels)[i * 2 + 1];
-
- U8* pix = (U8*)&scratch[i];
- pix[0] = pix[1] = pix[2] = lum;
- pix[3] = alpha;
- }
- }
-
- pixformat = GL_RGBA;
- intformat = GL_RGBA8;
- }
-
- if (pixformat == GL_LUMINANCE && pixtype == GL_UNSIGNED_BYTE)
- { //GL_LUMINANCE_ALPHA is deprecated, convert to RGB
- if (pixels != nullptr)
- {
- use_scratch = true;
- scratch = new(std::nothrow) U32[width * height];
- if (!scratch)
- {
- LLError::LLUserWarningMsg::showOutOfMemory();
- LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
- << " bytes for a manual image W" << width << " H" << height << LL_ENDL;
- }
-
- U32 pixel_count = (U32)(width * height);
- for (U32 i = 0; i < pixel_count; i++)
- {
- U8 lum = ((U8*)pixels)[i];
-
- U8* pix = (U8*)&scratch[i];
- pix[0] = pix[1] = pix[2] = lum;
- pix[3] = 255;
- }
- }
- pixformat = GL_RGBA;
- intformat = GL_RGB8;
- }
- }
-
- const bool compress = LLImageGL::sCompressTextures && allow_compression;
- if (compress)
- {
- switch (intformat)
- {
- case GL_RGB:
- case GL_RGB8:
- intformat = GL_COMPRESSED_RGB;
- break;
- case GL_SRGB:
- case GL_SRGB8:
- intformat = GL_COMPRESSED_SRGB;
- break;
- case GL_RGBA:
- case GL_RGBA8:
- intformat = GL_COMPRESSED_RGBA;
- break;
- case GL_SRGB_ALPHA:
- case GL_SRGB8_ALPHA8:
- intformat = GL_COMPRESSED_SRGB_ALPHA;
- break;
- case GL_LUMINANCE:
- case GL_LUMINANCE8:
- intformat = GL_COMPRESSED_LUMINANCE;
- break;
- case GL_LUMINANCE_ALPHA:
- case GL_LUMINANCE8_ALPHA8:
- intformat = GL_COMPRESSED_LUMINANCE_ALPHA;
- break;
- case GL_ALPHA:
- case GL_ALPHA8:
- intformat = GL_COMPRESSED_ALPHA;
- break;
- case GL_RED:
- case GL_R8:
- intformat = GL_COMPRESSED_RED;
- break;
- default:
- LL_WARNS() << "Could not compress format: " << std::hex << intformat << LL_ENDL;
- break;
- }
- }
-
- stop_glerror();
- {
- LL_PROFILE_ZONE_NAMED("glTexImage2D");
- LL_PROFILE_ZONE_NUM(width);
- LL_PROFILE_ZONE_NUM(height);
-
- free_cur_tex_image();
- const bool use_sub_image = should_stagger_image_set(compress);
- if (!use_sub_image)
- {
- LL_PROFILE_ZONE_NAMED("glTexImage2D alloc + copy");
- glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, use_scratch ? scratch : pixels);
- }
- else
- {
- // break up calls to a manageable size for the GL command buffer
- {
- LL_PROFILE_ZONE_NAMED("glTexImage2D alloc");
- glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, nullptr);
- }
-
- U8* src = (U8*)(use_scratch ? scratch : pixels);
- if (src)
- {
- LL_PROFILE_ZONE_NAMED("glTexImage2D copy");
- sub_image_lines(target, miplevel, 0, 0, width, height, pixformat, pixtype, src, width);
- }
- }
- alloc_tex_image(width, height, pixformat);
- }
- stop_glerror();
-
- if (use_scratch)
- {
- delete[] scratch;
- }
-}
-
-//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()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- checkActiveThread();
-
- if (gGLManager.mIsDisabled)
- {
- LL_WARNS() << "Trying to create a texture while GL is disabled!" << LL_ENDL;
- return false;
- }
-
- mGLTextureCreated = false ; //do not save this texture when gl is destroyed.
-
- llassert(gGLManager.mInited);
- stop_glerror();
-
- if(mTexName)
- {
- LLImageGL::deleteTextures(1, (reinterpret_cast<GLuint*>(&mTexName))) ;
- mTexName = 0;
- }
-
-
- LLImageGL::generateTextures(1, &mTexName);
- stop_glerror();
- if (!mTexName)
- {
- LL_WARNS() << "LLImageGL::createGLTexture failed to make an empty texture" << LL_ENDL;
- return false;
- }
-
- return true ;
-}
-
-bool LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/, bool to_create, S32 category, bool defer_copy, LLGLuint* tex_name)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- checkActiveThread();
-
- if (gGLManager.mIsDisabled)
- {
- LL_WARNS() << "Trying to create a texture while GL is disabled!" << LL_ENDL;
- return false;
- }
-
- llassert(gGLManager.mInited);
- stop_glerror();
-
- if (!imageraw || imageraw->isBufferInvalid())
- {
- LL_WARNS() << "Trying to create a texture from invalid image data" << LL_ENDL;
- mGLTextureCreated = false;
- return false;
- }
-
- if (discard_level < 0)
- {
- llassert(mCurrentDiscardLevel >= 0);
- discard_level = mCurrentDiscardLevel;
- }
-
- // Actual image width/height = raw image width/height * 2^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
- if (!setSize(w, h, imageraw->getComponents(), discard_level))
- {
- LL_WARNS() << "Trying to create a texture with incorrect dimensions!" << LL_ENDL;
- mGLTextureCreated = false;
- return false;
- }
-
- if (mHasExplicitFormat &&
- ((mFormatPrimary == GL_RGBA && mComponents < 4) ||
- (mFormatPrimary == GL_RGB && mComponents < 3)))
-
- {
- LL_WARNS() << "Incorrect format: " << std::hex << mFormatPrimary << " components: " << (U32)mComponents << LL_ENDL;
- mHasExplicitFormat = false;
- }
-
- 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:
- LL_ERRS() << "Bad number of components for texture: " << (U32)getComponents() << LL_ENDL;
- }
-
- calcAlphaChannelOffsetAndStride() ;
- }
-
- if(!to_create) //not create a gl texture
- {
- destroyGLTexture();
- mCurrentDiscardLevel = discard_level;
- mLastBindTime = sLastFrameTime;
- mGLTextureCreated = false;
- return true ;
- }
-
- setCategory(category);
- const U8* rawdata = imageraw->getData();
- return createGLTexture(discard_level, rawdata, false, usename, defer_copy, tex_name);
-}
-
-bool LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, bool data_hasmips, S32 usename, bool defer_copy, LLGLuint* tex_name)
-// Call with void data, vmem is allocated but unitialized
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- LL_PROFILE_GPU_ZONE("createGLTexture");
- checkActiveThread();
-
- bool main_thread = on_main_thread();
-
- if (defer_copy)
- {
- data_in = nullptr;
- }
- else
- {
- llassert(data_in);
- }
-
- stop_glerror();
-
- if (discard_level < 0)
- {
- llassert(mCurrentDiscardLevel >= 0);
- discard_level = mCurrentDiscardLevel;
- }
- discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
-
- if (main_thread // <--- always force creation of new_texname when not on main thread ...
- && !defer_copy // <--- ... or defer copy is set
- && mTexName != 0 && discard_level == mCurrentDiscardLevel)
- {
- LL_PROFILE_ZONE_NAMED("cglt - early setImage");
- // This will only be true if the size has not changed
- if (tex_name != nullptr)
- {
- *tex_name = mTexName;
- }
- return setImage(data_in, data_hasmips);
- }
-
- GLuint old_texname = mTexName;
- GLuint new_texname = 0;
- if (usename != 0)
- {
- llassert(main_thread);
- new_texname = usename;
- }
- else
- {
- LLImageGL::generateTextures(1, &new_texname);
- {
- gGL.getTexUnit(0)->bind(this, false, false, new_texname);
- glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_BASE_LEVEL, 0);
- glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_MAX_LEVEL, mMaxDiscardLevel - discard_level);
- }
- }
-
- if (tex_name != nullptr)
- {
- *tex_name = new_texname;
- }
-
- if (mUseMipMaps)
- {
- mAutoGenMips = true;
- }
-
- mCurrentDiscardLevel = discard_level;
-
- {
- LL_PROFILE_ZONE_NAMED("cglt - late setImage");
- if (!setImage(data_in, data_hasmips, new_texname))
- {
- return false;
- }
- }
-
- // 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
- gGL.getTexUnit(0)->unbind(mBindTarget);
-
- //if we're on the image loading thread, be sure to delete old_texname and update mTexName on the main thread
- if (!defer_copy)
- {
- if (!main_thread)
- {
- syncToMainThread(new_texname);
- }
- else
- {
- //not on background thread, immediately set mTexName
- if (old_texname != 0 && old_texname != new_texname)
- {
- LLImageGL::deleteTextures(1, &old_texname);
- }
- mTexName = new_texname;
- }
- }
-
-
- mTextureMemory = (S64Bytes)getMipBytes(mCurrentDiscardLevel);
- mTexelsInGLTexture = getWidth() * getHeight();
-
- // mark this as bound at this point, so we don't throw it out immediately
- mLastBindTime = sLastFrameTime;
-
- checkActiveThread();
- return true;
-}
-
-void LLImageGL::syncToMainThread(LLGLuint new_tex_name)
-{
- LL_PROFILE_ZONE_SCOPED;
- llassert(!on_main_thread());
-
- {
- LL_PROFILE_ZONE_NAMED("cglt - sync");
- if (gGLManager.mIsNVIDIA)
- {
- // wait for texture upload to finish before notifying main thread
- // upload is complete
- auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
- glFlush();
- glClientWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
- glDeleteSync(sync);
- }
- else
- {
- // post a sync to the main thread (will execute before tex name swap lambda below)
- // glFlush calls here are partly superstitious and partly backed by observation
- // on AMD hardware
- glFlush();
- auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
- glFlush();
- LL::WorkQueue::postMaybe(
- mMainQueue,
- [=]()
- {
- LL_PROFILE_ZONE_NAMED("cglt - wait sync");
- {
- LL_PROFILE_ZONE_NAMED("glWaitSync");
- glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
- }
- {
- LL_PROFILE_ZONE_NAMED("glDeleteSync");
- glDeleteSync(sync);
- }
- });
- }
- }
-
- ref();
- LL::WorkQueue::postMaybe(
- mMainQueue,
- [=]()
- {
- LL_PROFILE_ZONE_NAMED("cglt - delete callback");
- syncTexName(new_tex_name);
- unref();
- });
-
- LL_PROFILER_GPU_COLLECT;
-}
-
-
-void LLImageGL::syncTexName(LLGLuint texname)
-{
- if (texname != 0)
- {
- if (mTexName != 0 && mTexName != texname)
- {
- LLImageGL::deleteTextures(1, &mTexName);
- }
- mTexName = texname;
- }
-}
-
-bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const
-{
- llassert_always(sAllowReadBackRaw) ;
- //LL_ERRS() << "should not call this function!" << LL_ENDL ;
-
- if (discard_level < 0)
- {
- discard_level = mCurrentDiscardLevel;
- }
-
- if (mTexName == 0 || discard_level < mCurrentDiscardLevel || discard_level > mMaxDiscardLevel )
- {
- return false;
- }
-
- S32 gl_discard = discard_level - mCurrentDiscardLevel;
-
- //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);
- if (glwidth == 0)
- {
- // No mip data smaller than current discard level
- return false;
- }
-
- S32 width = getWidth(discard_level);
- S32 height = getHeight(discard_level);
- S32 ncomponents = getComponents();
- if (ncomponents == 0)
- {
- return false;
- }
- if(width < glwidth)
- {
- LL_WARNS() << "texture size is smaller than it should be." << LL_ENDL ;
- LL_WARNS() << "width: " << width << " glwidth: " << glwidth << " mWidth: " << mWidth <<
- " mCurrentDiscardLevel: " << (S32)mCurrentDiscardLevel << " discard_level: " << (S32)discard_level << LL_ENDL ;
- return false ;
- }
-
- if (width <= 0 || width > 2048 || height <= 0 || height > 2048 || ncomponents < 1 || ncomponents > 4)
- {
- LL_ERRS() << llformat("LLImageGL::readBackRaw: bogus params: %d x %d x %d",width,height,ncomponents) << LL_ENDL;
- }
-
- LLGLint is_compressed = 0;
- if (compressed_ok)
- {
- glGetTexLevelParameteriv(mTarget, is_compressed, GL_TEXTURE_COMPRESSED, (GLint*)&is_compressed);
- }
-
- //-----------------------------------------------------------------------------------------------
- GLenum error ;
- while((error = glGetError()) != GL_NO_ERROR)
- {
- LL_WARNS() << "GL Error happens before reading back texture. Error code: " << error << LL_ENDL ;
- }
- //-----------------------------------------------------------------------------------------------
-
- LLImageDataLock lock(imageraw);
-
- if (is_compressed)
- {
- LLGLint glbytes;
- glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, (GLint*)&glbytes);
- if(!imageraw->allocateDataSize(width, height, ncomponents, glbytes))
- {
- LL_WARNS() << "Memory allocation failed for reading back texture. Size is: " << glbytes << LL_ENDL ;
- LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ;
- return false ;
- }
-
- glGetCompressedTexImage(mTarget, gl_discard, (GLvoid*)(imageraw->getData()));
- //stop_glerror();
- }
- else
- {
- if(!imageraw->allocateDataSize(width, height, ncomponents))
- {
- LL_WARNS() << "Memory allocation failed for reading back texture." << LL_ENDL ;
- LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ;
- return false ;
- }
-
- glGetTexImage(GL_TEXTURE_2D, gl_discard, mFormatPrimary, mFormatType, (GLvoid*)(imageraw->getData()));
- //stop_glerror();
- }
-
- //-----------------------------------------------------------------------------------------------
- if((error = glGetError()) != GL_NO_ERROR)
- {
- LL_WARNS() << "GL Error happens after reading back texture. Error code: " << error << LL_ENDL ;
- imageraw->deleteData() ;
-
- while((error = glGetError()) != GL_NO_ERROR)
- {
- LL_WARNS() << "GL Error happens after reading back texture. Error code: " << error << LL_ENDL ;
- }
-
- return false ;
- }
- //-----------------------------------------------------------------------------------------------
-
- return true ;
-}
-
-void LLImageGL::destroyGLTexture()
-{
- checkActiveThread();
-
- if (mTexName != 0)
- {
- if(mTextureMemory != S64Bytes(0))
- {
- mTextureMemory = (S64Bytes)0;
- }
-
- LLImageGL::deleteTextures(1, &mTexName);
- mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
- mTexName = 0;
- mGLTextureCreated = false ;
- }
-}
-
-//force to invalidate the gl texture, most likely a sculpty texture
-void LLImageGL::forceToInvalidateGLTexture()
-{
- checkActiveThread();
- if (mTexName != 0)
- {
- destroyGLTexture();
- }
- else
- {
- mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
- }
-}
-
-//----------------------------------------------------------------------------
-
-void LLImageGL::setAddressMode(LLTexUnit::eTextureAddressMode mode)
-{
- if (mAddressMode != mode)
- {
- mTexOptionsDirty = true;
- mAddressMode = mode;
- }
-
- if (gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->getCurrTexture() == mTexName)
- {
- gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->setTextureAddressMode(mode);
- mTexOptionsDirty = false;
- }
-}
-
-void LLImageGL::setFilteringOption(LLTexUnit::eTextureFilterOptions option)
-{
- if (mFilterOption != option)
- {
- mTexOptionsDirty = true;
- mFilterOption = option;
- }
-
- if (mTexName != 0 && gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->getCurrTexture() == mTexName)
- {
- gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->setTextureFilteringOption(option);
- mTexOptionsDirty = false;
- stop_glerror();
- }
-}
-
-bool LLImageGL::getIsResident(bool test_now)
-{
- if (test_now)
- {
- if (mTexName != 0)
- {
- glAreTexturesResident(1, (GLuint*)&mTexName, &mIsResident);
- }
- else
- {
- mIsResident = false;
- }
- }
-
- return mIsResident;
-}
-
-S32 LLImageGL::getHeight(S32 discard_level) const
-{
- if (discard_level < 0)
- {
- discard_level = mCurrentDiscardLevel;
- }
- S32 height = mHeight >> discard_level;
- if (height < 1) height = 1;
- return height;
-}
-
-S32 LLImageGL::getWidth(S32 discard_level) const
-{
- if (discard_level < 0)
- {
- discard_level = mCurrentDiscardLevel;
- }
- S32 width = mWidth >> discard_level;
- if (width < 1) width = 1;
- return width;
-}
-
-S64 LLImageGL::getBytes(S32 discard_level) const
-{
- if (discard_level < 0)
- {
- discard_level = mCurrentDiscardLevel;
- }
- S32 w = mWidth>>discard_level;
- S32 h = mHeight>>discard_level;
- if (w == 0) w = 1;
- if (h == 0) h = 1;
- return dataFormatBytes(mFormatPrimary, w, h);
-}
-
-S64 LLImageGL::getMipBytes(S32 discard_level) const
-{
- if (discard_level < 0)
- {
- discard_level = mCurrentDiscardLevel;
- }
- S32 w = mWidth>>discard_level;
- S32 h = mHeight>>discard_level;
- S64 res = dataFormatBytes(mFormatPrimary, w, h);
- if (mUseMipMaps)
- {
- while (w > 1 && h > 1)
- {
- w >>= 1; if (w == 0) w = 1;
- h >>= 1; if (h == 0) h = 1;
- res += dataFormatBytes(mFormatPrimary, w, h);
- }
- }
- return res;
-}
-
-bool LLImageGL::isJustBound() const
-{
- return sLastFrameTime - mLastBindTime < 0.5f;
-}
-
-bool LLImageGL::getBoundRecently() const
-{
- return (bool)(sLastFrameTime - mLastBindTime < MIN_TEXTURE_LIFETIME);
-}
-
-bool LLImageGL::getIsAlphaMask() const
-{
- llassert_always(!sSkipAnalyzeAlpha);
- return mIsMask;
-}
-
-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_RED:
- case GL_RGB:
- case GL_SRGB:
- mNeedsAlphaAndPickMask = false;
- mIsMask = false;
- return; //no alpha channel.
- case GL_RGBA:
- case GL_SRGB_ALPHA:
- 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
- {
- LL_WARNS() << "Cannot analyze alpha for image with format type " << std::hex << mFormatType << std::dec << LL_ENDL;
-
- mNeedsAlphaAndPickMask = false ;
- mIsMask = false;
- }
-}
-
-void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h)
-{
- if(sSkipAnalyzeAlpha || !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 = 2; i < 13; 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/48 || // 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;
- }
-}
-
-//----------------------------------------------------------------------------
-U32 LLImageGL::createPickMask(S32 pWidth, S32 pHeight)
-{
- U32 pick_width = pWidth/2 + 1;
- U32 pick_height = pHeight/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);
-
- return size;
-}
-
-//----------------------------------------------------------------------------
-void LLImageGL::freePickMask()
-{
- // pickmask validity depends on old image size, delete it
- if (mPickMask != NULL)
- {
- delete [] mPickMask;
- }
- mPickMask = NULL;
- mPickMaskWidth = mPickMaskHeight = 0;
-}
-
-bool LLImageGL::isCompressed()
-{
- llassert(mFormatPrimary != 0);
- // *NOTE: Not all compressed formats are included here.
- bool is_compressed = false;
- switch (mFormatPrimary)
- {
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
- is_compressed = true;
- break;
- default:
- break;
- }
- return is_compressed;
-}
-
-//----------------------------------------------------------------------------
-void LLImageGL::updatePickMask(S32 width, S32 height, const U8* data_in)
-{
- if(!mNeedsAlphaAndPickMask)
- {
- return ;
- }
-
- freePickMask();
-
- if (mFormatType != GL_UNSIGNED_BYTE ||
- ((mFormatPrimary != GL_RGBA)
- && (mFormatPrimary != GL_SRGB_ALPHA)))
- {
- //cannot generate a pick mask for this texture
- return;
- }
-
-#ifdef SHOW_ASSERT
- const U32 pickSize = createPickMask(width, height);
-#else // SHOW_ASSERT
- createPickMask(width, height);
-#endif // SHOW_ASSERT
-
- 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 < pickSize);
-
- 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)) != 0;
- }
-
- return res;
-}
-
-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 ;
-}
-//----------------------------------------------------------------------------
-#if LL_IMAGEGL_THREAD_CHECK
-void LLImageGL::checkActiveThread()
-{
- llassert(mActiveThread == LLThread::currentID());
-}
-#endif
-
-//----------------------------------------------------------------------------
-
-
-// Manual Mip Generation
-/*
- S32 width = getWidth(discard_level);
- S32 height = getHeight(discard_level);
- S32 w = width, h = height;
- S32 nummips = 1;
- while (w > 4 && h > 4)
- {
- w >>= 1; h >>= 1;
- nummips++;
- }
- stop_glerror();
- w = width, h = height;
- const U8* prev_mip_data = 0;
- const U8* cur_mip_data = 0;
- for (int m=0; m<nummips; m++)
- {
- if (m==0)
- {
- cur_mip_data = rawdata;
- }
- else
- {
- S32 bytes = w * h * mComponents;
- U8* new_data = new U8[bytes];
- LLImageBase::generateMip(prev_mip_data, new_data, w, h, mComponents);
- cur_mip_data = new_data;
- }
- llassert(w > 0 && h > 0 && cur_mip_data);
- U8 test = cur_mip_data[w*h*mComponents-1];
- {
- LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data);
- stop_glerror();
- }
- if (prev_mip_data && prev_mip_data != rawdata)
- {
- delete prev_mip_data;
- }
- prev_mip_data = cur_mip_data;
- w >>= 1;
- h >>= 1;
- }
- if (prev_mip_data && prev_mip_data != rawdata)
- {
- delete prev_mip_data;
- }
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, nummips);
-*/
-
-LLImageGLThread::LLImageGLThread(LLWindow* window)
- // We want exactly one thread.
- : LL::ThreadPool("LLImageGL", 1)
- , mWindow(window)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- mFinished = false;
-
- mContext = mWindow->createSharedContext();
- LL::ThreadPool::start();
-}
-
-void LLImageGLThread::run()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- // We must perform setup on this thread before actually servicing our
- // WorkQueue, likewise cleanup afterwards.
- mWindow->makeContextCurrent(mContext);
- gGL.init(false);
- LL::ThreadPool::run();
- gGL.shutdown();
- mWindow->destroySharedContext(mContext);
-}
-
+/**
+ * @file llimagegl.cpp
+ * @brief Generic GL image handler
+ *
+ * $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$
+ */
+
+
+// TODO: create 2 classes for images w/ and w/o discard levels?
+
+#include "linden_common.h"
+
+#include "llimagegl.h"
+
+#include "llerror.h"
+#include "llfasttimer.h"
+#include "llimage.h"
+
+#include "llmath.h"
+#include "llgl.h"
+#include "llglslshader.h"
+#include "llrender.h"
+#include "llwindow.h"
+#include "llframetimer.h"
+
+extern LL_COMMON_API bool on_main_thread();
+
+#if !LL_IMAGEGL_THREAD_CHECK
+#define checkActiveThread()
+#endif
+
+//----------------------------------------------------------------------------
+const F32 MIN_TEXTURE_LIFETIME = 10.f;
+
+//which power of 2 is i?
+//assumes i is a power of 2 > 0
+U32 wpo2(U32 i);
+
+
+// texture memory accounting (for macOS)
+static LLMutex sTexMemMutex;
+static std::unordered_map<U32, U64> sTextureAllocs;
+static U64 sTextureBytes = 0;
+
+// track a texture alloc on the currently bound texture.
+// asserts that no currently tracked alloc exists
+void LLImageGLMemory::alloc_tex_image(U32 width, U32 height, U32 pixformat)
+{
+ U32 texUnit = gGL.getCurrentTexUnitIndex();
+ U32 texName = gGL.getTexUnit(texUnit)->getCurrTexture();
+ U64 size = LLImageGL::dataFormatBytes(pixformat, width, height);
+
+ llassert(size >= 0);
+
+ sTexMemMutex.lock();
+ llassert(sTextureAllocs.find(texName) == sTextureAllocs.end());
+
+ sTextureAllocs[texName] = size;
+ sTextureBytes += size;
+
+ sTexMemMutex.unlock();
+}
+
+// track texture free on given texName
+void LLImageGLMemory::free_tex_image(U32 texName)
+{
+ sTexMemMutex.lock();
+ auto iter = sTextureAllocs.find(texName);
+ if (iter != sTextureAllocs.end())
+ {
+ llassert(iter->second <= sTextureBytes); // sTextureBytes MUST NOT go below zero
+
+ sTextureBytes -= iter->second;
+
+ sTextureAllocs.erase(iter);
+ }
+
+ sTexMemMutex.unlock();
+}
+
+// track texture free on given texNames
+void LLImageGLMemory::free_tex_images(U32 count, const U32* texNames)
+{
+ for (int i = 0; i < count; ++i)
+ {
+ free_tex_image(texNames[i]);
+ }
+}
+
+// track texture free on currently bound texture
+void LLImageGLMemory::free_cur_tex_image()
+{
+ U32 texUnit = gGL.getCurrentTexUnitIndex();
+ U32 texName = gGL.getTexUnit(texUnit)->getCurrTexture();
+ free_tex_image(texName);
+}
+
+using namespace LLImageGLMemory;
+
+// static
+U64 LLImageGL::getTextureBytesAllocated()
+{
+ return sTextureBytes;
+}
+
+//statics
+
+U32 LLImageGL::sUniqueCount = 0;
+U32 LLImageGL::sBindCount = 0;
+S32 LLImageGL::sCount = 0;
+
+bool LLImageGL::sGlobalUseAnisotropic = false;
+F32 LLImageGL::sLastFrameTime = 0.f;
+bool LLImageGL::sAllowReadBackRaw = false ;
+LLImageGL* LLImageGL::sDefaultGLTexture = NULL ;
+bool LLImageGL::sCompressTextures = false;
+std::set<LLImageGL*> LLImageGL::sImageList;
+
+
+bool LLImageGLThread::sEnabledTextures = false;
+bool LLImageGLThread::sEnabledMedia = false;
+
+//****************************************************************************************************
+//The below for texture auditing use only
+//****************************************************************************************************
+//-----------------------
+//debug use
+S32 LLImageGL::sCurTexSizeBar = -1 ;
+S32 LLImageGL::sCurTexPickSize = -1 ;
+S32 LLImageGL::sMaxCategories = 1 ;
+
+//optimization for when we don't need to calculate mIsMask
+bool LLImageGL::sSkipAnalyzeAlpha;
+
+//------------------------
+//****************************************************************************************************
+//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)
+ {
+ LL_INFOS() << "Bound: " << texname << " Should bind: " << mTexName << " Default: " << LLImageGL::sDefaultGLTexture->getTexName() << LL_ENDL;
+
+ error = true;
+ if (gDebugSession)
+ {
+ gFailLog << "Invalid texture bound!" << std::endl;
+ }
+ else
+ {
+ LL_ERRS() << "Invalid texture bound!" << LL_ENDL;
+ }
+ }
+ 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
+ {
+ LL_ERRS() << "wrong texture size and discard level: width: " <<
+ mWidth << " Height: " << mHeight << " Current Level: " << (S32)mCurrentDiscardLevel << LL_ENDL ;
+ }
+ }
+
+ 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(LLWindow* window, S32 num_catagories, bool skip_analyze_alpha /* = false */, bool thread_texture_loads /* = false */, bool thread_media_updates /* = false */)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ sSkipAnalyzeAlpha = skip_analyze_alpha;
+
+ if (thread_texture_loads || thread_media_updates)
+ {
+ LLImageGLThread::createInstance(window);
+ LLImageGLThread::sEnabledTextures = thread_texture_loads;
+ LLImageGLThread::sEnabledMedia = thread_media_updates;
+ }
+}
+
+//static
+void LLImageGL::cleanupClass()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ LLImageGLThread::deleteSingleton();
+}
+
+
+//static
+S32 LLImageGL::dataFormatBits(S32 dataformat)
+{
+ switch (dataformat)
+ {
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return 4;
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return 4;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return 8;
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return 8;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return 8;
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return 8;
+ case GL_LUMINANCE: return 8;
+ case GL_ALPHA: return 8;
+ case GL_RED: return 8;
+ case GL_COLOR_INDEX: return 8;
+ case GL_LUMINANCE_ALPHA: return 16;
+ case GL_RGB: return 24;
+ case GL_SRGB: return 24;
+ case GL_RGB8: return 24;
+ case GL_RGBA: return 32;
+ case GL_SRGB_ALPHA: return 32;
+ case GL_BGRA: return 32; // Used for QuickTime media textures on the Mac
+ case GL_DEPTH_COMPONENT: return 24;
+ case GL_RGB16F: return 48;
+ case GL_RGBA16F: return 64;
+ default:
+ LL_ERRS() << "LLImageGL::Unknown format: " << dataformat << LL_ENDL;
+ return 0;
+ }
+}
+
+//static
+S64 LLImageGL::dataFormatBytes(S32 dataformat, S32 width, S32 height)
+{
+ switch (dataformat)
+ {
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
+ if (width < 4) width = 4;
+ if (height < 4) height = 4;
+ break;
+ default:
+ break;
+ }
+ S64 bytes (((S64)width * (S64)height * (S64)dataFormatBits(dataformat)+7)>>3);
+ S64 aligned = (bytes+3)&~3;
+ return aligned;
+}
+
+//static
+S32 LLImageGL::dataFormatComponents(S32 dataformat)
+{
+ switch (dataformat)
+ {
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return 3;
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return 3;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return 4;
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return 4;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return 4;
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return 4;
+ case GL_LUMINANCE: return 1;
+ case GL_ALPHA: return 1;
+ case GL_RED: return 1;
+ case GL_COLOR_INDEX: return 1;
+ case GL_LUMINANCE_ALPHA: return 2;
+ case GL_RGB: return 3;
+ case GL_SRGB: return 3;
+ case GL_RGBA: return 4;
+ case GL_SRGB_ALPHA: return 4;
+ case GL_BGRA: return 4; // Used for QuickTime media textures on the Mac
+ default:
+ LL_ERRS() << "LLImageGL::Unknown format: " << dataformat << LL_ENDL;
+ return 0;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// static
+void LLImageGL::updateStats(F32 current_time)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ sLastFrameTime = current_time;
+}
+
+//----------------------------------------------------------------------------
+
+//static
+void LLImageGL::destroyGL(bool save_state)
+{
+ for (S32 stage = 0; stage < gGLManager.mNumTextureImageUnits; stage++)
+ {
+ 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)
+ {
+ if (save_state && glimage->isGLTextureCreated() && glimage->mComponents)
+ {
+ glimage->mSaveData = new LLImageRaw;
+ if(!glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData, false)) //necessary, keep it.
+ {
+ glimage->mSaveData = NULL ;
+ }
+ }
+
+ glimage->destroyGLTexture();
+ stop_glerror();
+ }
+ }
+ sAllowReadBackRaw = false ;
+}
+
+//static
+void LLImageGL::restoreGL()
+{
+ for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
+ iter != sImageList.end(); iter++)
+ {
+ LLImageGL* glimage = *iter;
+ if(glimage->getTexName())
+ {
+ LL_ERRS() << "tex name is not 0." << LL_ENDL ;
+ }
+ if (glimage->mSaveData.notNull())
+ {
+ if (glimage->getComponents() && glimage->mSaveData->getComponents())
+ {
+ glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData, 0, true, glimage->getCategory());
+ stop_glerror();
+ }
+ glimage->mSaveData = NULL; // deletes data
+ }
+ }
+}
+
+//static
+void LLImageGL::dirtyTexOptions()
+{
+ for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
+ iter != sImageList.end(); iter++)
+ {
+ LLImageGL* glimage = *iter;
+ glimage->mTexOptionsDirty = true;
+ stop_glerror();
+ }
+
+}
+//----------------------------------------------------------------------------
+
+//for server side use only.
+//static
+bool LLImageGL::create(LLPointer<LLImageGL>& dest, bool usemipmaps)
+{
+ dest = new LLImageGL(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);
+ return true;
+}
+
+//----------------------------------------------------------------------------
+
+LLImageGL::LLImageGL(bool usemipmaps)
+: mSaveData(0), mExternalTexture(false)
+{
+ init(usemipmaps);
+ setSize(0, 0, 0);
+ sImageList.insert(this);
+ sCount++;
+}
+
+LLImageGL::LLImageGL(U32 width, U32 height, U8 components, bool usemipmaps)
+: mSaveData(0), mExternalTexture(false)
+{
+ llassert( components <= 4 );
+ init(usemipmaps);
+ setSize(width, height, components);
+ sImageList.insert(this);
+ sCount++;
+}
+
+LLImageGL::LLImageGL(const LLImageRaw* imageraw, bool usemipmaps)
+: mSaveData(0), mExternalTexture(false)
+{
+ init(usemipmaps);
+ setSize(0, 0, 0);
+ sImageList.insert(this);
+ sCount++;
+
+ createGLTexture(0, imageraw);
+}
+
+LLImageGL::LLImageGL(
+ LLGLuint texName,
+ U32 components,
+ LLGLenum target,
+ LLGLint formatInternal,
+ LLGLenum formatPrimary,
+ LLGLenum formatType,
+ LLTexUnit::eTextureAddressMode addressMode)
+{
+ init(false);
+ mTexName = texName;
+ mTarget = target;
+ mComponents = components;
+ mAddressMode = addressMode;
+ mFormatType = formatType;
+ mFormatInternal = formatInternal;
+ mFormatPrimary = formatPrimary;
+}
+
+
+LLImageGL::~LLImageGL()
+{
+ if (!mExternalTexture && gGLManager.mInited)
+ {
+ LLImageGL::cleanup();
+ sImageList.erase(this);
+ freePickMask();
+ sCount--;
+ }
+}
+
+void LLImageGL::init(bool usemipmaps)
+{
+#if LL_IMAGEGL_THREAD_CHECK
+ mActiveThread = LLThread::currentID();
+#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 = S64Bytes(0);
+ mLastBindTime = 0.f;
+
+ mPickMask = NULL;
+ mPickMaskWidth = 0;
+ mPickMaskHeight = 0;
+ mUseMipMaps = usemipmaps;
+ mHasExplicitFormat = 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 ;
+
+ mAllowCompression = true;
+
+ mTarget = GL_TEXTURE_2D;
+ mBindTarget = LLTexUnit::TT_TEXTURE;
+ mHasMipMaps = false;
+ mMipLevels = -1;
+
+ mIsResident = 0;
+
+ mComponents = 0;
+ mMaxDiscardLevel = MAX_DISCARD_LEVEL;
+
+ mTexOptionsDirty = true;
+ mAddressMode = LLTexUnit::TAM_WRAP;
+ mFilterOption = LLTexUnit::TFO_ANISOTROPIC;
+
+ mFormatInternal = -1;
+ mFormatPrimary = (LLGLenum) 0;
+ mFormatType = GL_UNSIGNED_BYTE;
+ mFormatSwapBytes = false;
+
+#ifdef DEBUG_MISS
+ mMissed = false;
+#endif
+
+ mCategory = -1;
+
+ // Sometimes we have to post work for the main thread.
+ mMainQueue = LL::WorkQueue::getInstance("mainloop");
+}
+
+void LLImageGL::cleanup()
+{
+ if (!gGLManager.mIsDisabled)
+ {
+ destroyGLTexture();
+ }
+ freePickMask();
+
+ mSaveData = NULL; // deletes data
+}
+
+//----------------------------------------------------------------------------
+
+//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)
+{
+ if(dim < 0)
+ {
+ return false ;
+ }
+ if(!dim)//0 is a power-of-two number
+ {
+ return true ;
+ }
+ return !(dim & (dim - 1)) ;
+}
+
+//static
+bool LLImageGL::checkSize(S32 width, S32 height)
+{
+ return check_power_of_two(width) && check_power_of_two(height);
+}
+
+bool LLImageGL::setSize(S32 width, S32 height, S32 ncomponents, S32 discard_level)
+{
+ if (width != mWidth || height != mHeight || ncomponents != mComponents)
+ {
+ // Check if dimensions are a power of two!
+ if (!checkSize(width, height))
+ {
+ LL_WARNS() << llformat("Texture has non power of two dimension: %dx%d",width,height) << LL_ENDL;
+ return false;
+ }
+
+ // pickmask validity depends on old image size, delete it
+ freePickMask();
+
+ mWidth = width;
+ mHeight = height;
+ mComponents = ncomponents;
+ if (ncomponents > 0)
+ {
+ mMaxDiscardLevel = 0;
+ while (width > 1 && height > 1 && mMaxDiscardLevel < MAX_DISCARD_LEVEL)
+ {
+ mMaxDiscardLevel++;
+ width >>= 1;
+ height >>= 1;
+ }
+
+ if(discard_level > 0)
+ {
+ mMaxDiscardLevel = llmax(mMaxDiscardLevel, (S8)discard_level);
+ }
+ }
+ else
+ {
+ mMaxDiscardLevel = MAX_DISCARD_LEVEL;
+ }
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+
+// virtual
+void LLImageGL::dump()
+{
+ LL_INFOS() << "mMaxDiscardLevel " << S32(mMaxDiscardLevel)
+ << " mLastBindTime " << mLastBindTime
+ << " mTarget " << S32(mTarget)
+ << " mBindTarget " << S32(mBindTarget)
+ << " mUseMipMaps " << S32(mUseMipMaps)
+ << " mHasMipMaps " << S32(mHasMipMaps)
+ << " mCurrentDiscardLevel " << S32(mCurrentDiscardLevel)
+ << " mFormatInternal " << S32(mFormatInternal)
+ << " mFormatPrimary " << S32(mFormatPrimary)
+ << " mFormatType " << S32(mFormatType)
+ << " mFormatSwapBytes " << S32(mFormatSwapBytes)
+ << " mHasExplicitFormat " << S32(mHasExplicitFormat)
+#if DEBUG_MISS
+ << " mMissed " << mMissed
+#endif
+ << LL_ENDL;
+
+ LL_INFOS() << " mTextureMemory " << mTextureMemory
+ << " mTexNames " << mTexName
+ << " mIsResident " << S32(mIsResident)
+ << LL_ENDL;
+}
+
+//----------------------------------------------------------------------------
+void LLImageGL::forceUpdateBindStats(void) const
+{
+ mLastBindTime = sLastFrameTime;
+}
+
+bool LLImageGL::updateBindStats() const
+{
+ if (mTexName != 0)
+ {
+#ifdef DEBUG_MISS
+ mMissed = ! getIsResident(true);
+#endif
+ sBindCount++;
+ if (mLastBindTime != sLastFrameTime)
+ {
+ // we haven't accounted for this texture yet this frame
+ sUniqueCount++;
+ mLastBindTime = sLastFrameTime;
+
+ return true ;
+ }
+ }
+ return false ;
+}
+
+F32 LLImageGL::getTimePassedSinceLastBound()
+{
+ return sLastFrameTime - mLastBindTime ;
+}
+
+void LLImageGL::setExplicitFormat( LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, bool swap_bytes )
+{
+ // Note: must be called before createTexture()
+ // Note: it's up to the caller to ensure that the format matches the number of components.
+ mHasExplicitFormat = true;
+ mFormatInternal = internal_format;
+ mFormatPrimary = primary_format;
+ if(type_format == 0)
+ mFormatType = GL_UNSIGNED_BYTE;
+ else
+ mFormatType = type_format;
+ mFormatSwapBytes = swap_bytes;
+
+ calcAlphaChannelOffsetAndStride() ;
+}
+
+//----------------------------------------------------------------------------
+
+void LLImageGL::setImage(const LLImageRaw* imageraw)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ llassert((imageraw->getWidth() == getWidth(mCurrentDiscardLevel)) &&
+ (imageraw->getHeight() == getHeight(mCurrentDiscardLevel)) &&
+ (imageraw->getComponents() == getComponents()));
+ const U8* rawdata = imageraw->getData();
+ setImage(rawdata, false);
+}
+
+bool LLImageGL::setImage(const U8* data_in, bool data_hasmips /* = false */, S32 usename /* = 0 */)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+
+ const bool is_compressed = isCompressed();
+
+ if (mUseMipMaps)
+ {
+ //set has mip maps to true before binding image so tex parameters get set properly
+ gGL.getTexUnit(0)->unbind(mBindTarget);
+
+ mHasMipMaps = true;
+ mTexOptionsDirty = true;
+ setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
+ }
+ else
+ {
+ mHasMipMaps = false;
+ }
+
+ gGL.getTexUnit(0)->bind(this, false, false, usename);
+
+ if (data_in == nullptr)
+ {
+ S32 w = getWidth();
+ S32 h = getHeight();
+ LLImageGL::setManualImage(mTarget, 0, mFormatInternal, w, h,
+ mFormatPrimary, mFormatType, (GLvoid*)data_in, mAllowCompression);
+ }
+ else if (mUseMipMaps)
+ {
+ 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;
+
+ mMipLevels = llmax(mMipLevels, gl_level);
+
+ if (d > mCurrentDiscardLevel)
+ {
+ data_in -= dataFormatBytes(mFormatPrimary, w, h); // see above comment
+ }
+ if (is_compressed)
+ {
+ S32 tex_size = dataFormatBytes(mFormatPrimary, w, h);
+ glCompressedTexImage2D(mTarget, gl_level, mFormatPrimary, w, h, 0, tex_size, (GLvoid *)data_in);
+ stop_glerror();
+ }
+ else
+ {
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
+ stop_glerror();
+ }
+
+ LLImageGL::setManualImage(mTarget, gl_level, mFormatInternal, w, h, mFormatPrimary, GL_UNSIGNED_BYTE, (GLvoid*)data_in, mAllowCompression);
+ if (gl_level == 0)
+ {
+ analyzeAlpha(data_in, w, h);
+ }
+ updatePickMask(w, h, data_in);
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
+ stop_glerror();
+ }
+
+ stop_glerror();
+ }
+ stop_glerror();
+ }
+ }
+ else if (!is_compressed)
+ {
+ if (mAutoGenMips)
+ {
+ stop_glerror();
+ {
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
+ stop_glerror();
+ }
+
+ S32 w = getWidth(mCurrentDiscardLevel);
+ S32 h = getHeight(mCurrentDiscardLevel);
+
+ mMipLevels = wpo2(llmax(w, h));
+
+ //use legacy mipmap generation mode (note: making this condional can cause rendering issues)
+ // -- but making it not conditional triggers deprecation warnings when core profile is enabled
+ // (some rendering issues while core profile is enabled are acceptable at this point in time)
+ if (!LLRender::sGLCoreProfile)
+ {
+ glTexParameteri(mTarget, GL_GENERATE_MIPMAP, GL_TRUE);
+ }
+
+ LLImageGL::setManualImage(mTarget, 0, mFormatInternal,
+ w, h,
+ mFormatPrimary, mFormatType,
+ data_in, mAllowCompression);
+ analyzeAlpha(data_in, w, h);
+ stop_glerror();
+
+ updatePickMask(w, h, data_in);
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
+ stop_glerror();
+ }
+
+ if (LLRender::sGLCoreProfile)
+ {
+ LL_PROFILE_GPU_ZONE("generate mip map");
+ glGenerateMipmap(mTarget);
+ }
+ stop_glerror();
+ }
+ }
+ else
+ {
+ // Create mips by hand
+ // ~4x faster than gluBuild2DMipmaps
+ S32 width = getWidth(mCurrentDiscardLevel);
+ S32 height = getHeight(mCurrentDiscardLevel);
+ S32 nummips = mMaxDiscardLevel - mCurrentDiscardLevel + 1;
+ S32 w = width, h = height;
+
+
+ const U8* new_data = 0;
+ (void)new_data;
+
+ const U8* prev_mip_data = 0;
+ const U8* cur_mip_data = 0;
+#ifdef SHOW_ASSERT
+ S32 cur_mip_size = 0;
+#endif
+ mMipLevels = nummips;
+
+ for (int m=0; m<nummips; m++)
+ {
+ if (m==0)
+ {
+ cur_mip_data = data_in;
+#ifdef SHOW_ASSERT
+ cur_mip_size = width * height * mComponents;
+#endif
+ }
+ else
+ {
+ S32 bytes = w * h * mComponents;
+#ifdef SHOW_ASSERT
+ llassert(prev_mip_data);
+ llassert(cur_mip_size == bytes*4);
+#endif
+ U8* new_data = new(std::nothrow) U8[bytes];
+ if (!new_data)
+ {
+ stop_glerror();
+
+ if (prev_mip_data)
+ {
+ if (prev_mip_data != cur_mip_data)
+ delete[] prev_mip_data;
+ prev_mip_data = nullptr;
+ }
+ if (cur_mip_data)
+ {
+ delete[] cur_mip_data;
+ cur_mip_data = nullptr;
+ }
+
+ mGLTextureCreated = false;
+ return false;
+ }
+ else
+ {
+
+#ifdef SHOW_ASSERT
+ llassert(prev_mip_data);
+ llassert(cur_mip_size == bytes * 4);
+#endif
+
+ LLImageBase::generateMip(prev_mip_data, new_data, w, h, mComponents);
+ cur_mip_data = new_data;
+#ifdef SHOW_ASSERT
+ cur_mip_size = bytes;
+#endif
+ }
+
+ }
+ llassert(w > 0 && h > 0 && cur_mip_data);
+ (void)cur_mip_data;
+ {
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
+ stop_glerror();
+ }
+
+ LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data, mAllowCompression);
+ if (m == 0)
+ {
+ analyzeAlpha(data_in, w, h);
+ }
+ stop_glerror();
+ if (m == 0)
+ {
+ updatePickMask(w, h, cur_mip_data);
+ }
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
+ stop_glerror();
+ }
+ }
+ if (prev_mip_data && prev_mip_data != data_in)
+ {
+ delete[] prev_mip_data;
+ }
+ prev_mip_data = cur_mip_data;
+ w >>= 1;
+ h >>= 1;
+ }
+ if (prev_mip_data && prev_mip_data != data_in)
+ {
+ delete[] prev_mip_data;
+ prev_mip_data = NULL;
+ }
+ }
+ }
+ else
+ {
+ LL_ERRS() << "Compressed Image has mipmaps but data does not (can not auto generate compressed mips)" << LL_ENDL;
+ }
+ }
+ else
+ {
+ mMipLevels = 0;
+ S32 w = getWidth();
+ S32 h = getHeight();
+ if (is_compressed)
+ {
+ S32 tex_size = dataFormatBytes(mFormatPrimary, w, h);
+ glCompressedTexImage2D(mTarget, 0, mFormatPrimary, w, h, 0, tex_size, (GLvoid *)data_in);
+ stop_glerror();
+ }
+ else
+ {
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
+ stop_glerror();
+ }
+
+ LLImageGL::setManualImage(mTarget, 0, mFormatInternal, w, h,
+ mFormatPrimary, mFormatType, (GLvoid *)data_in, mAllowCompression);
+ analyzeAlpha(data_in, w, h);
+
+ updatePickMask(w, h, data_in);
+
+ stop_glerror();
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
+ stop_glerror();
+ }
+
+ }
+ }
+ stop_glerror();
+ mGLTextureCreated = true;
+ return true;
+}
+
+bool LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image)
+{
+ //not compatible with core GL profile
+ llassert(!LLRender::sGLCoreProfile);
+
+ if (gGLManager.mIsDisabled)
+ {
+ LL_WARNS() << "Trying to create a texture while GL is disabled!" << LL_ENDL;
+ return false;
+ }
+ llassert(gGLManager.mInited);
+ stop_glerror();
+
+ if (discard_level < 0)
+ {
+ llassert(mCurrentDiscardLevel >= 0);
+ discard_level = mCurrentDiscardLevel;
+ }
+
+ // 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
+ if (!setSize(w, h, raw_image->getComponents(), discard_level))
+ {
+ LL_WARNS() << "Trying to create a texture with incorrect dimensions!" << LL_ENDL;
+ return false;
+ }
+
+ 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:
+ LL_ERRS() << "Bad number of components for texture: " << (U32) getComponents() << LL_ENDL;
+ }
+ }
+
+ 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 ;
+}
+
+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();
+}
+
+U32 type_width_from_pixtype(U32 pixtype)
+{
+ U32 type_width = 0;
+ switch (pixtype)
+ {
+ case GL_UNSIGNED_BYTE:
+ case GL_BYTE:
+ case GL_UNSIGNED_INT_8_8_8_8_REV:
+ type_width = 1;
+ break;
+ case GL_UNSIGNED_SHORT:
+ case GL_SHORT:
+ type_width = 2;
+ break;
+ case GL_UNSIGNED_INT:
+ case GL_INT:
+ case GL_FLOAT:
+ type_width = 4;
+ break;
+ default:
+ LL_ERRS() << "Unknown type: " << pixtype << LL_ENDL;
+ }
+ return type_width;
+}
+
+bool should_stagger_image_set(bool compressed)
+{
+#if LL_DARWIN
+ return false;
+#else
+ // glTexSubImage2D doesn't work with compressed textures on select tested Nvidia GPUs on Windows 10 -Cosmic,2023-03-08
+ // Setting media textures off-thread seems faster when not using sub_image_lines (Nvidia/Windows 10) -Cosmic,2023-03-31
+ return !compressed && on_main_thread();
+#endif
+}
+
+// Equivalent to calling glSetSubImage2D(target, miplevel, x_offset, y_offset, width, height, pixformat, pixtype, src), assuming the total width of the image is data_width
+// However, instead there are multiple calls to glSetSubImage2D on smaller slices of the image
+void sub_image_lines(U32 target, S32 miplevel, S32 x_offset, S32 y_offset, S32 width, S32 height, U32 pixformat, U32 pixtype, const U8* src, S32 data_width)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+
+ U32 components = LLImageGL::dataFormatComponents(pixformat);
+ U32 type_width = type_width_from_pixtype(pixtype);
+
+ const U32 line_width = data_width * components * type_width;
+ const U32 y_offset_end = y_offset + height;
+ for (U32 y_pos = y_offset; y_pos < y_offset_end; ++y_pos)
+ {
+ glTexSubImage2D(target, miplevel, x_offset, y_pos, width, 1, pixformat, pixtype, src);
+ src += line_width;
+ }
+}
+
+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 /* = false */, LLGLuint use_name)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ if (!width || !height)
+ {
+ return true;
+ }
+ LLGLuint tex_name = use_name != 0 ? use_name : mTexName;
+ if (0 == tex_name)
+ {
+ // *TODO: Re-enable warning? Ran into thread locking issues? DK 2011-02-18
+ //LL_WARNS() << "Setting subimage on image without GL texture" << LL_ENDL;
+ return false;
+ }
+ if (datap == NULL)
+ {
+ // *TODO: Re-enable warning? Ran into thread locking issues? DK 2011-02-18
+ //LL_WARNS() << "Setting subimage on image with NULL datap" << LL_ENDL;
+ return false;
+ }
+
+ // 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, tex_name);
+ }
+ else
+ {
+ if (mUseMipMaps)
+ {
+ dump();
+ LL_ERRS() << "setSubImage called with mipmapped image (not supported)" << LL_ENDL;
+ }
+ llassert_always(mCurrentDiscardLevel == 0);
+ llassert_always(x_pos >= 0 && y_pos >= 0);
+
+ if (((x_pos + width) > getWidth()) ||
+ (y_pos + height) > getHeight())
+ {
+ dump();
+ LL_ERRS() << "Subimage not wholly in target image!"
+ << " x_pos " << x_pos
+ << " y_pos " << y_pos
+ << " width " << width
+ << " height " << height
+ << " getWidth() " << getWidth()
+ << " getHeight() " << getHeight()
+ << LL_ENDL;
+ }
+
+ if ((x_pos + width) > data_width ||
+ (y_pos + height) > data_height)
+ {
+ dump();
+ LL_ERRS() << "Subimage not wholly in source image!"
+ << " x_pos " << x_pos
+ << " y_pos " << y_pos
+ << " width " << width
+ << " height " << height
+ << " source_width " << data_width
+ << " source_height " << data_height
+ << LL_ENDL;
+ }
+
+
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, data_width);
+ stop_glerror();
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
+ stop_glerror();
+ }
+
+ const U8* sub_datap = datap + (y_pos * data_width + x_pos) * getComponents();
+ // Update the GL texture
+ bool res = gGL.getTexUnit(0)->bindManual(mBindTarget, tex_name);
+ if (!res) LL_ERRS() << "LLImageGL::setSubImage(): bindTexture failed" << LL_ENDL;
+ stop_glerror();
+
+ const bool use_sub_image = should_stagger_image_set(isCompressed());
+ if (!use_sub_image)
+ {
+ // *TODO: Why does this work here, in setSubImage, but not in
+ // setManualImage? Maybe because it only gets called with the
+ // dimensions of the full image? Or because the image is never
+ // compressed?
+ glTexSubImage2D(mTarget, 0, x_pos, y_pos, width, height, mFormatPrimary, mFormatType, sub_datap);
+ }
+ else
+ {
+ sub_image_lines(mTarget, 0, x_pos, y_pos, width, height, mFormatPrimary, mFormatType, sub_datap, data_width);
+ }
+ gGL.getTexUnit(0)->disable();
+ stop_glerror();
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
+ stop_glerror();
+ }
+
+ 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 force_fast_update /* = false */, LLGLuint use_name)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height, force_fast_update, use_name);
+}
+
+// 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 (gGL.getTexUnit(0)->bind(this, false, true))
+ {
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, fb_x, fb_y, x_pos, y_pos, width, height);
+ mGLTextureCreated = true;
+ stop_glerror();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// static
+void LLImageGL::generateTextures(S32 numTextures, U32 *textures)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ static constexpr U32 pool_size = 1024;
+ static thread_local U32 name_pool[pool_size]; // pool of texture names
+ static thread_local U32 name_count = 0; // number of available names in the pool
+
+ if (name_count == 0)
+ {
+ LL_PROFILE_ZONE_NAMED("iglgt - reup pool");
+ // pool is emtpy, refill it
+ glGenTextures(pool_size, name_pool);
+ name_count = pool_size;
+ }
+
+ if (numTextures <= name_count)
+ {
+ //copy teture names off the end of the pool
+ memcpy(textures, name_pool + name_count - numTextures, sizeof(U32) * numTextures);
+ name_count -= numTextures;
+ }
+ else
+ {
+ LL_PROFILE_ZONE_NAMED("iglgt - pool miss");
+ glGenTextures(numTextures, textures);
+ }
+}
+
+// static
+void LLImageGL::deleteTextures(S32 numTextures, const U32 *textures)
+{
+ if (gGLManager.mInited)
+ {
+ free_tex_images(numTextures, textures);
+ glDeleteTextures(numTextures, textures);
+ }
+}
+
+// static
+void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void* pixels, bool allow_compression)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ bool use_scratch = false;
+ U32* scratch = NULL;
+ if (LLRender::sGLCoreProfile)
+ {
+ if (pixformat == GL_ALPHA && pixtype == GL_UNSIGNED_BYTE)
+ { //GL_ALPHA is deprecated, convert to RGBA
+ if (pixels != nullptr)
+ {
+ use_scratch = true;
+ scratch = new(std::nothrow) U32[width * height];
+ if (!scratch)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
+ << " bytes for a manual image W" << width << " H" << height << LL_ENDL;
+ }
+
+ U32 pixel_count = (U32)(width * height);
+ for (U32 i = 0; i < pixel_count; i++)
+ {
+ U8* pix = (U8*)&scratch[i];
+ pix[0] = pix[1] = pix[2] = 0;
+ pix[3] = ((U8*)pixels)[i];
+ }
+ }
+
+ pixformat = GL_RGBA;
+ intformat = GL_RGBA8;
+ }
+
+ if (pixformat == GL_LUMINANCE_ALPHA && pixtype == GL_UNSIGNED_BYTE)
+ { //GL_LUMINANCE_ALPHA is deprecated, convert to RGBA
+ if (pixels != nullptr)
+ {
+ use_scratch = true;
+ scratch = new(std::nothrow) U32[width * height];
+ if (!scratch)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
+ << " bytes for a manual image W" << width << " H" << height << LL_ENDL;
+ }
+
+ U32 pixel_count = (U32)(width * height);
+ for (U32 i = 0; i < pixel_count; i++)
+ {
+ U8 lum = ((U8*)pixels)[i * 2 + 0];
+ U8 alpha = ((U8*)pixels)[i * 2 + 1];
+
+ U8* pix = (U8*)&scratch[i];
+ pix[0] = pix[1] = pix[2] = lum;
+ pix[3] = alpha;
+ }
+ }
+
+ pixformat = GL_RGBA;
+ intformat = GL_RGBA8;
+ }
+
+ if (pixformat == GL_LUMINANCE && pixtype == GL_UNSIGNED_BYTE)
+ { //GL_LUMINANCE_ALPHA is deprecated, convert to RGB
+ if (pixels != nullptr)
+ {
+ use_scratch = true;
+ scratch = new(std::nothrow) U32[width * height];
+ if (!scratch)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
+ << " bytes for a manual image W" << width << " H" << height << LL_ENDL;
+ }
+
+ U32 pixel_count = (U32)(width * height);
+ for (U32 i = 0; i < pixel_count; i++)
+ {
+ U8 lum = ((U8*)pixels)[i];
+
+ U8* pix = (U8*)&scratch[i];
+ pix[0] = pix[1] = pix[2] = lum;
+ pix[3] = 255;
+ }
+ }
+ pixformat = GL_RGBA;
+ intformat = GL_RGB8;
+ }
+ }
+
+ const bool compress = LLImageGL::sCompressTextures && allow_compression;
+ if (compress)
+ {
+ switch (intformat)
+ {
+ case GL_RGB:
+ case GL_RGB8:
+ intformat = GL_COMPRESSED_RGB;
+ break;
+ case GL_SRGB:
+ case GL_SRGB8:
+ intformat = GL_COMPRESSED_SRGB;
+ break;
+ case GL_RGBA:
+ case GL_RGBA8:
+ intformat = GL_COMPRESSED_RGBA;
+ break;
+ case GL_SRGB_ALPHA:
+ case GL_SRGB8_ALPHA8:
+ intformat = GL_COMPRESSED_SRGB_ALPHA;
+ break;
+ case GL_LUMINANCE:
+ case GL_LUMINANCE8:
+ intformat = GL_COMPRESSED_LUMINANCE;
+ break;
+ case GL_LUMINANCE_ALPHA:
+ case GL_LUMINANCE8_ALPHA8:
+ intformat = GL_COMPRESSED_LUMINANCE_ALPHA;
+ break;
+ case GL_ALPHA:
+ case GL_ALPHA8:
+ intformat = GL_COMPRESSED_ALPHA;
+ break;
+ case GL_RED:
+ case GL_R8:
+ intformat = GL_COMPRESSED_RED;
+ break;
+ default:
+ LL_WARNS() << "Could not compress format: " << std::hex << intformat << LL_ENDL;
+ break;
+ }
+ }
+
+ stop_glerror();
+ {
+ LL_PROFILE_ZONE_NAMED("glTexImage2D");
+ LL_PROFILE_ZONE_NUM(width);
+ LL_PROFILE_ZONE_NUM(height);
+
+ free_cur_tex_image();
+ const bool use_sub_image = should_stagger_image_set(compress);
+ if (!use_sub_image)
+ {
+ LL_PROFILE_ZONE_NAMED("glTexImage2D alloc + copy");
+ glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, use_scratch ? scratch : pixels);
+ }
+ else
+ {
+ // break up calls to a manageable size for the GL command buffer
+ {
+ LL_PROFILE_ZONE_NAMED("glTexImage2D alloc");
+ glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, nullptr);
+ }
+
+ U8* src = (U8*)(use_scratch ? scratch : pixels);
+ if (src)
+ {
+ LL_PROFILE_ZONE_NAMED("glTexImage2D copy");
+ sub_image_lines(target, miplevel, 0, 0, width, height, pixformat, pixtype, src, width);
+ }
+ }
+ alloc_tex_image(width, height, pixformat);
+ }
+ stop_glerror();
+
+ if (use_scratch)
+ {
+ delete[] scratch;
+ }
+}
+
+//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()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ checkActiveThread();
+
+ if (gGLManager.mIsDisabled)
+ {
+ LL_WARNS() << "Trying to create a texture while GL is disabled!" << LL_ENDL;
+ return false;
+ }
+
+ mGLTextureCreated = false ; //do not save this texture when gl is destroyed.
+
+ llassert(gGLManager.mInited);
+ stop_glerror();
+
+ if(mTexName)
+ {
+ LLImageGL::deleteTextures(1, (reinterpret_cast<GLuint*>(&mTexName))) ;
+ mTexName = 0;
+ }
+
+
+ LLImageGL::generateTextures(1, &mTexName);
+ stop_glerror();
+ if (!mTexName)
+ {
+ LL_WARNS() << "LLImageGL::createGLTexture failed to make an empty texture" << LL_ENDL;
+ return false;
+ }
+
+ return true ;
+}
+
+bool LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/, bool to_create, S32 category, bool defer_copy, LLGLuint* tex_name)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ checkActiveThread();
+
+ if (gGLManager.mIsDisabled)
+ {
+ LL_WARNS() << "Trying to create a texture while GL is disabled!" << LL_ENDL;
+ return false;
+ }
+
+ llassert(gGLManager.mInited);
+ stop_glerror();
+
+ if (!imageraw || imageraw->isBufferInvalid())
+ {
+ LL_WARNS() << "Trying to create a texture from invalid image data" << LL_ENDL;
+ mGLTextureCreated = false;
+ return false;
+ }
+
+ if (discard_level < 0)
+ {
+ llassert(mCurrentDiscardLevel >= 0);
+ discard_level = mCurrentDiscardLevel;
+ }
+
+ // Actual image width/height = raw image width/height * 2^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
+ if (!setSize(w, h, imageraw->getComponents(), discard_level))
+ {
+ LL_WARNS() << "Trying to create a texture with incorrect dimensions!" << LL_ENDL;
+ mGLTextureCreated = false;
+ return false;
+ }
+
+ if (mHasExplicitFormat &&
+ ((mFormatPrimary == GL_RGBA && mComponents < 4) ||
+ (mFormatPrimary == GL_RGB && mComponents < 3)))
+
+ {
+ LL_WARNS() << "Incorrect format: " << std::hex << mFormatPrimary << " components: " << (U32)mComponents << LL_ENDL;
+ mHasExplicitFormat = false;
+ }
+
+ 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:
+ LL_ERRS() << "Bad number of components for texture: " << (U32)getComponents() << LL_ENDL;
+ }
+
+ calcAlphaChannelOffsetAndStride() ;
+ }
+
+ if(!to_create) //not create a gl texture
+ {
+ destroyGLTexture();
+ mCurrentDiscardLevel = discard_level;
+ mLastBindTime = sLastFrameTime;
+ mGLTextureCreated = false;
+ return true ;
+ }
+
+ setCategory(category);
+ const U8* rawdata = imageraw->getData();
+ return createGLTexture(discard_level, rawdata, false, usename, defer_copy, tex_name);
+}
+
+bool LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, bool data_hasmips, S32 usename, bool defer_copy, LLGLuint* tex_name)
+// Call with void data, vmem is allocated but unitialized
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ LL_PROFILE_GPU_ZONE("createGLTexture");
+ checkActiveThread();
+
+ bool main_thread = on_main_thread();
+
+ if (defer_copy)
+ {
+ data_in = nullptr;
+ }
+ else
+ {
+ llassert(data_in);
+ }
+
+ stop_glerror();
+
+ if (discard_level < 0)
+ {
+ llassert(mCurrentDiscardLevel >= 0);
+ discard_level = mCurrentDiscardLevel;
+ }
+ discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
+
+ if (main_thread // <--- always force creation of new_texname when not on main thread ...
+ && !defer_copy // <--- ... or defer copy is set
+ && mTexName != 0 && discard_level == mCurrentDiscardLevel)
+ {
+ LL_PROFILE_ZONE_NAMED("cglt - early setImage");
+ // This will only be true if the size has not changed
+ if (tex_name != nullptr)
+ {
+ *tex_name = mTexName;
+ }
+ return setImage(data_in, data_hasmips);
+ }
+
+ GLuint old_texname = mTexName;
+ GLuint new_texname = 0;
+ if (usename != 0)
+ {
+ llassert(main_thread);
+ new_texname = usename;
+ }
+ else
+ {
+ LLImageGL::generateTextures(1, &new_texname);
+ {
+ gGL.getTexUnit(0)->bind(this, false, false, new_texname);
+ glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_MAX_LEVEL, mMaxDiscardLevel - discard_level);
+ }
+ }
+
+ if (tex_name != nullptr)
+ {
+ *tex_name = new_texname;
+ }
+
+ if (mUseMipMaps)
+ {
+ mAutoGenMips = true;
+ }
+
+ mCurrentDiscardLevel = discard_level;
+
+ {
+ LL_PROFILE_ZONE_NAMED("cglt - late setImage");
+ if (!setImage(data_in, data_hasmips, new_texname))
+ {
+ return false;
+ }
+ }
+
+ // 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
+ gGL.getTexUnit(0)->unbind(mBindTarget);
+
+ //if we're on the image loading thread, be sure to delete old_texname and update mTexName on the main thread
+ if (!defer_copy)
+ {
+ if (!main_thread)
+ {
+ syncToMainThread(new_texname);
+ }
+ else
+ {
+ //not on background thread, immediately set mTexName
+ if (old_texname != 0 && old_texname != new_texname)
+ {
+ LLImageGL::deleteTextures(1, &old_texname);
+ }
+ mTexName = new_texname;
+ }
+ }
+
+
+ mTextureMemory = (S64Bytes)getMipBytes(mCurrentDiscardLevel);
+ mTexelsInGLTexture = getWidth() * getHeight();
+
+ // mark this as bound at this point, so we don't throw it out immediately
+ mLastBindTime = sLastFrameTime;
+
+ checkActiveThread();
+ return true;
+}
+
+void LLImageGL::syncToMainThread(LLGLuint new_tex_name)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ llassert(!on_main_thread());
+
+ {
+ LL_PROFILE_ZONE_NAMED("cglt - sync");
+ if (gGLManager.mIsNVIDIA)
+ {
+ // wait for texture upload to finish before notifying main thread
+ // upload is complete
+ auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ glFlush();
+ glClientWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
+ glDeleteSync(sync);
+ }
+ else
+ {
+ // post a sync to the main thread (will execute before tex name swap lambda below)
+ // glFlush calls here are partly superstitious and partly backed by observation
+ // on AMD hardware
+ glFlush();
+ auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ glFlush();
+ LL::WorkQueue::postMaybe(
+ mMainQueue,
+ [=]()
+ {
+ LL_PROFILE_ZONE_NAMED("cglt - wait sync");
+ {
+ LL_PROFILE_ZONE_NAMED("glWaitSync");
+ glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
+ }
+ {
+ LL_PROFILE_ZONE_NAMED("glDeleteSync");
+ glDeleteSync(sync);
+ }
+ });
+ }
+ }
+
+ ref();
+ LL::WorkQueue::postMaybe(
+ mMainQueue,
+ [=]()
+ {
+ LL_PROFILE_ZONE_NAMED("cglt - delete callback");
+ syncTexName(new_tex_name);
+ unref();
+ });
+
+ LL_PROFILER_GPU_COLLECT;
+}
+
+
+void LLImageGL::syncTexName(LLGLuint texname)
+{
+ if (texname != 0)
+ {
+ if (mTexName != 0 && mTexName != texname)
+ {
+ LLImageGL::deleteTextures(1, &mTexName);
+ }
+ mTexName = texname;
+ }
+}
+
+bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const
+{
+ llassert_always(sAllowReadBackRaw) ;
+ //LL_ERRS() << "should not call this function!" << LL_ENDL ;
+
+ if (discard_level < 0)
+ {
+ discard_level = mCurrentDiscardLevel;
+ }
+
+ if (mTexName == 0 || discard_level < mCurrentDiscardLevel || discard_level > mMaxDiscardLevel )
+ {
+ return false;
+ }
+
+ S32 gl_discard = discard_level - mCurrentDiscardLevel;
+
+ //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);
+ if (glwidth == 0)
+ {
+ // No mip data smaller than current discard level
+ return false;
+ }
+
+ S32 width = getWidth(discard_level);
+ S32 height = getHeight(discard_level);
+ S32 ncomponents = getComponents();
+ if (ncomponents == 0)
+ {
+ return false;
+ }
+ if(width < glwidth)
+ {
+ LL_WARNS() << "texture size is smaller than it should be." << LL_ENDL ;
+ LL_WARNS() << "width: " << width << " glwidth: " << glwidth << " mWidth: " << mWidth <<
+ " mCurrentDiscardLevel: " << (S32)mCurrentDiscardLevel << " discard_level: " << (S32)discard_level << LL_ENDL ;
+ return false ;
+ }
+
+ if (width <= 0 || width > 2048 || height <= 0 || height > 2048 || ncomponents < 1 || ncomponents > 4)
+ {
+ LL_ERRS() << llformat("LLImageGL::readBackRaw: bogus params: %d x %d x %d",width,height,ncomponents) << LL_ENDL;
+ }
+
+ LLGLint is_compressed = 0;
+ if (compressed_ok)
+ {
+ glGetTexLevelParameteriv(mTarget, is_compressed, GL_TEXTURE_COMPRESSED, (GLint*)&is_compressed);
+ }
+
+ //-----------------------------------------------------------------------------------------------
+ GLenum error ;
+ while((error = glGetError()) != GL_NO_ERROR)
+ {
+ LL_WARNS() << "GL Error happens before reading back texture. Error code: " << error << LL_ENDL ;
+ }
+ //-----------------------------------------------------------------------------------------------
+
+ LLImageDataLock lock(imageraw);
+
+ if (is_compressed)
+ {
+ LLGLint glbytes;
+ glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, (GLint*)&glbytes);
+ if(!imageraw->allocateDataSize(width, height, ncomponents, glbytes))
+ {
+ LL_WARNS() << "Memory allocation failed for reading back texture. Size is: " << glbytes << LL_ENDL ;
+ LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ;
+ return false ;
+ }
+
+ glGetCompressedTexImage(mTarget, gl_discard, (GLvoid*)(imageraw->getData()));
+ //stop_glerror();
+ }
+ else
+ {
+ if(!imageraw->allocateDataSize(width, height, ncomponents))
+ {
+ LL_WARNS() << "Memory allocation failed for reading back texture." << LL_ENDL ;
+ LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ;
+ return false ;
+ }
+
+ glGetTexImage(GL_TEXTURE_2D, gl_discard, mFormatPrimary, mFormatType, (GLvoid*)(imageraw->getData()));
+ //stop_glerror();
+ }
+
+ //-----------------------------------------------------------------------------------------------
+ if((error = glGetError()) != GL_NO_ERROR)
+ {
+ LL_WARNS() << "GL Error happens after reading back texture. Error code: " << error << LL_ENDL ;
+ imageraw->deleteData() ;
+
+ while((error = glGetError()) != GL_NO_ERROR)
+ {
+ LL_WARNS() << "GL Error happens after reading back texture. Error code: " << error << LL_ENDL ;
+ }
+
+ return false ;
+ }
+ //-----------------------------------------------------------------------------------------------
+
+ return true ;
+}
+
+void LLImageGL::destroyGLTexture()
+{
+ checkActiveThread();
+
+ if (mTexName != 0)
+ {
+ if(mTextureMemory != S64Bytes(0))
+ {
+ mTextureMemory = (S64Bytes)0;
+ }
+
+ LLImageGL::deleteTextures(1, &mTexName);
+ mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
+ mTexName = 0;
+ mGLTextureCreated = false ;
+ }
+}
+
+//force to invalidate the gl texture, most likely a sculpty texture
+void LLImageGL::forceToInvalidateGLTexture()
+{
+ checkActiveThread();
+ if (mTexName != 0)
+ {
+ destroyGLTexture();
+ }
+ else
+ {
+ mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
+ }
+}
+
+//----------------------------------------------------------------------------
+
+void LLImageGL::setAddressMode(LLTexUnit::eTextureAddressMode mode)
+{
+ if (mAddressMode != mode)
+ {
+ mTexOptionsDirty = true;
+ mAddressMode = mode;
+ }
+
+ if (gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->getCurrTexture() == mTexName)
+ {
+ gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->setTextureAddressMode(mode);
+ mTexOptionsDirty = false;
+ }
+}
+
+void LLImageGL::setFilteringOption(LLTexUnit::eTextureFilterOptions option)
+{
+ if (mFilterOption != option)
+ {
+ mTexOptionsDirty = true;
+ mFilterOption = option;
+ }
+
+ if (mTexName != 0 && gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->getCurrTexture() == mTexName)
+ {
+ gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->setTextureFilteringOption(option);
+ mTexOptionsDirty = false;
+ stop_glerror();
+ }
+}
+
+bool LLImageGL::getIsResident(bool test_now)
+{
+ if (test_now)
+ {
+ if (mTexName != 0)
+ {
+ glAreTexturesResident(1, (GLuint*)&mTexName, &mIsResident);
+ }
+ else
+ {
+ mIsResident = false;
+ }
+ }
+
+ return mIsResident;
+}
+
+S32 LLImageGL::getHeight(S32 discard_level) const
+{
+ if (discard_level < 0)
+ {
+ discard_level = mCurrentDiscardLevel;
+ }
+ S32 height = mHeight >> discard_level;
+ if (height < 1) height = 1;
+ return height;
+}
+
+S32 LLImageGL::getWidth(S32 discard_level) const
+{
+ if (discard_level < 0)
+ {
+ discard_level = mCurrentDiscardLevel;
+ }
+ S32 width = mWidth >> discard_level;
+ if (width < 1) width = 1;
+ return width;
+}
+
+S64 LLImageGL::getBytes(S32 discard_level) const
+{
+ if (discard_level < 0)
+ {
+ discard_level = mCurrentDiscardLevel;
+ }
+ S32 w = mWidth>>discard_level;
+ S32 h = mHeight>>discard_level;
+ if (w == 0) w = 1;
+ if (h == 0) h = 1;
+ return dataFormatBytes(mFormatPrimary, w, h);
+}
+
+S64 LLImageGL::getMipBytes(S32 discard_level) const
+{
+ if (discard_level < 0)
+ {
+ discard_level = mCurrentDiscardLevel;
+ }
+ S32 w = mWidth>>discard_level;
+ S32 h = mHeight>>discard_level;
+ S64 res = dataFormatBytes(mFormatPrimary, w, h);
+ if (mUseMipMaps)
+ {
+ while (w > 1 && h > 1)
+ {
+ w >>= 1; if (w == 0) w = 1;
+ h >>= 1; if (h == 0) h = 1;
+ res += dataFormatBytes(mFormatPrimary, w, h);
+ }
+ }
+ return res;
+}
+
+bool LLImageGL::isJustBound() const
+{
+ return sLastFrameTime - mLastBindTime < 0.5f;
+}
+
+bool LLImageGL::getBoundRecently() const
+{
+ return (bool)(sLastFrameTime - mLastBindTime < MIN_TEXTURE_LIFETIME);
+}
+
+bool LLImageGL::getIsAlphaMask() const
+{
+ llassert_always(!sSkipAnalyzeAlpha);
+ return mIsMask;
+}
+
+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_RED:
+ case GL_RGB:
+ case GL_SRGB:
+ mNeedsAlphaAndPickMask = false;
+ mIsMask = false;
+ return; //no alpha channel.
+ case GL_RGBA:
+ case GL_SRGB_ALPHA:
+ 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
+ {
+ LL_WARNS() << "Cannot analyze alpha for image with format type " << std::hex << mFormatType << std::dec << LL_ENDL;
+
+ mNeedsAlphaAndPickMask = false ;
+ mIsMask = false;
+ }
+}
+
+void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h)
+{
+ if(sSkipAnalyzeAlpha || !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 = 2; i < 13; 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/48 || // 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;
+ }
+}
+
+//----------------------------------------------------------------------------
+U32 LLImageGL::createPickMask(S32 pWidth, S32 pHeight)
+{
+ U32 pick_width = pWidth/2 + 1;
+ U32 pick_height = pHeight/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);
+
+ return size;
+}
+
+//----------------------------------------------------------------------------
+void LLImageGL::freePickMask()
+{
+ // pickmask validity depends on old image size, delete it
+ if (mPickMask != NULL)
+ {
+ delete [] mPickMask;
+ }
+ mPickMask = NULL;
+ mPickMaskWidth = mPickMaskHeight = 0;
+}
+
+bool LLImageGL::isCompressed()
+{
+ llassert(mFormatPrimary != 0);
+ // *NOTE: Not all compressed formats are included here.
+ bool is_compressed = false;
+ switch (mFormatPrimary)
+ {
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
+ is_compressed = true;
+ break;
+ default:
+ break;
+ }
+ return is_compressed;
+}
+
+//----------------------------------------------------------------------------
+void LLImageGL::updatePickMask(S32 width, S32 height, const U8* data_in)
+{
+ if(!mNeedsAlphaAndPickMask)
+ {
+ return ;
+ }
+
+ freePickMask();
+
+ if (mFormatType != GL_UNSIGNED_BYTE ||
+ ((mFormatPrimary != GL_RGBA)
+ && (mFormatPrimary != GL_SRGB_ALPHA)))
+ {
+ //cannot generate a pick mask for this texture
+ return;
+ }
+
+#ifdef SHOW_ASSERT
+ const U32 pickSize = createPickMask(width, height);
+#else // SHOW_ASSERT
+ createPickMask(width, height);
+#endif // SHOW_ASSERT
+
+ 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 < pickSize);
+
+ 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)) != 0;
+ }
+
+ return res;
+}
+
+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 ;
+}
+//----------------------------------------------------------------------------
+#if LL_IMAGEGL_THREAD_CHECK
+void LLImageGL::checkActiveThread()
+{
+ llassert(mActiveThread == LLThread::currentID());
+}
+#endif
+
+//----------------------------------------------------------------------------
+
+
+// Manual Mip Generation
+/*
+ S32 width = getWidth(discard_level);
+ S32 height = getHeight(discard_level);
+ S32 w = width, h = height;
+ S32 nummips = 1;
+ while (w > 4 && h > 4)
+ {
+ w >>= 1; h >>= 1;
+ nummips++;
+ }
+ stop_glerror();
+ w = width, h = height;
+ const U8* prev_mip_data = 0;
+ const U8* cur_mip_data = 0;
+ for (int m=0; m<nummips; m++)
+ {
+ if (m==0)
+ {
+ cur_mip_data = rawdata;
+ }
+ else
+ {
+ S32 bytes = w * h * mComponents;
+ U8* new_data = new U8[bytes];
+ LLImageBase::generateMip(prev_mip_data, new_data, w, h, mComponents);
+ cur_mip_data = new_data;
+ }
+ llassert(w > 0 && h > 0 && cur_mip_data);
+ U8 test = cur_mip_data[w*h*mComponents-1];
+ {
+ LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data);
+ stop_glerror();
+ }
+ if (prev_mip_data && prev_mip_data != rawdata)
+ {
+ delete prev_mip_data;
+ }
+ prev_mip_data = cur_mip_data;
+ w >>= 1;
+ h >>= 1;
+ }
+ if (prev_mip_data && prev_mip_data != rawdata)
+ {
+ delete prev_mip_data;
+ }
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, nummips);
+*/
+
+LLImageGLThread::LLImageGLThread(LLWindow* window)
+ // We want exactly one thread.
+ : LL::ThreadPool("LLImageGL", 1)
+ , mWindow(window)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ mFinished = false;
+
+ mContext = mWindow->createSharedContext();
+ LL::ThreadPool::start();
+}
+
+void LLImageGLThread::run()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+ // We must perform setup on this thread before actually servicing our
+ // WorkQueue, likewise cleanup afterwards.
+ mWindow->makeContextCurrent(mContext);
+ gGL.init(false);
+ LL::ThreadPool::run();
+ gGL.shutdown();
+ mWindow->destroySharedContext(mContext);
+}
+
diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h
index 68df7b0684..5c7a5ce821 100644
--- a/indra/llrender/llimagegl.h
+++ b/indra/llrender/llimagegl.h
@@ -1,362 +1,362 @@
-/**
- * @file llimagegl.h
- * @brief Object for managing images and their textures
- *
- * $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_LLIMAGEGL_H
-#define LL_LLIMAGEGL_H
-
-#include "llimage.h"
-
-#include "llgltypes.h"
-#include "llpointer.h"
-#include "llrefcount.h"
-#include "v2math.h"
-#include "llunits.h"
-#include "llthreadsafequeue.h"
-#include "llrender.h"
-#include "threadpool.h"
-#include "workqueue.h"
-
-#define LL_IMAGEGL_THREAD_CHECK 0 //set to 1 to enable thread debugging for ImageGL
-
-class LLWindow;
-
-#define BYTES_TO_MEGA_BYTES(x) ((x) >> 20)
-#define MEGA_BYTES_TO_BYTES(x) ((x) << 20)
-
-namespace LLImageGLMemory
-{
- void alloc_tex_image(U32 width, U32 height, U32 pixformat);
- void free_tex_image(U32 texName);
- void free_tex_images(U32 count, const U32* texNames);
- void free_cur_tex_image();
-}
-
-//============================================================================
-class LLImageGL : public LLRefCount
-{
- friend class LLTexUnit;
-public:
-
- // Get an estimate of how many bytes have been allocated in vram for textures.
- // Does not include mipmaps.
- // NOTE: multiplying this number by two gives a good estimate for total
- // video memory usage based on testing in lagland against an NVIDIA GPU.
- static U64 getTextureBytesAllocated();
-
- // These 2 functions replace glGenTextures() and glDeleteTextures()
- static void generateTextures(S32 numTextures, U32 *textures);
- static void deleteTextures(S32 numTextures, const U32 *textures);
-
- // Size calculation
- static S32 dataFormatBits(S32 dataformat);
- static S64 dataFormatBytes(S32 dataformat, S32 width, S32 height);
- static S32 dataFormatComponents(S32 dataformat);
-
- bool updateBindStats() const ;
- F32 getTimePassedSinceLastBound();
- void forceUpdateBindStats(void) const;
-
- // needs to be called every frame
- static void updateStats(F32 current_time);
-
- // Save off / restore GL textures
- static void destroyGL(bool save_state = true);
- static void restoreGL();
- static void dirtyTexOptions();
-
- 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);
- LLImageGL(const LLImageRaw* imageraw, bool usemipmaps = true);
-
- // For wrapping textures created via GL elsewhere with our API only. Use with caution.
- LLImageGL(LLGLuint mTexName, U32 components, LLGLenum target, LLGLint formatInternal, LLGLenum formatPrimary, LLGLenum formatType, LLTexUnit::eTextureAddressMode addressMode);
-
-protected:
- virtual ~LLImageGL();
-
- void analyzeAlpha(const void* data_in, U32 w, U32 h);
- void calcAlphaChannelOffsetAndStride();
-
-public:
- virtual void dump(); // debugging info to LL_INFOS()
-
- bool setSize(S32 width, S32 height, S32 ncomponents, S32 discard_level = -1);
- void setComponents(S32 ncomponents) { mComponents = (S8)ncomponents ;}
- void setAllowCompression(bool allow) { mAllowCompression = allow; }
-
- static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression = true);
-
- bool createGLTexture() ;
- bool createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, bool to_create = true,
- S32 category = sMaxCategories-1, bool defer_copy = false, LLGLuint* tex_name = nullptr);
- bool createGLTexture(S32 discard_level, const U8* data, bool data_hasmips = false, S32 usename = 0, bool defer_copy = false, LLGLuint* tex_name = nullptr);
- void setImage(const LLImageRaw* imageraw);
- bool setImage(const U8* data_in, bool data_hasmips = false, S32 usename = 0);
- // *TODO: This function may not work if the textures is compressed (i.e.
- // RenderCompressTextures is 0). Partial image updates do not work on
- // compressed textures.
- bool setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, bool force_fast_update = false, LLGLuint use_name = 0);
- 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, LLGLuint use_name = 0);
- bool setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height);
-
- // wait for gl commands to finish on current thread and push
- // a lambda to main thread to swap mNewTexName and mTexName
- void syncToMainThread(LLGLuint new_tex_name);
-
- // Read back a raw image for this discard level, if it exists
- bool readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const;
- void destroyGLTexture();
- void forceToInvalidateGLTexture();
-
- void setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format = 0, bool swap_bytes = false);
- 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; }
- S64 getBytes(S32 discard_level = -1) const;
- S64 getMipBytes(S32 discard_level = -1) const;
- bool getBoundRecently() const;
- bool isJustBound() const;
- bool getHasExplicitFormat() const { return mHasExplicitFormat; }
- LLGLenum getPrimaryFormat() const { return mFormatPrimary; }
- LLGLenum getFormatType() const { return mFormatType; }
-
- bool getHasGLTexture() const { return mTexName != 0; }
- LLGLuint getTexName() const { return mTexName; }
-
- bool getIsAlphaMask() const;
-
- bool getIsResident(bool test_now = false); // not const
-
- 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; }
- void setHasMipMaps(bool hasmips) { mHasMipMaps = hasmips; }
- void updatePickMask(S32 width, S32 height, const U8* data_in);
- bool getMask(const LLVector2 &tc);
-
- 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() ;
-
-#if LL_IMAGEGL_THREAD_CHECK
- // thread debugging
- std::thread::id mActiveThread;
- void checkActiveThread();
-#endif
-
-public:
- // Various GL/Rendering options
- S64Bytes mTextureMemory;
- mutable F32 mLastBindTime; // last time this was bound, by discard level
-
-private:
- U32 createPickMask(S32 pWidth, S32 pHeight);
- void freePickMask();
- bool isCompressed();
-
- LLPointer<LLImageRaw> mSaveData; // used for destroyGL/restoreGL
- LL::WorkQueue::weak_t mMainQueue;
- U8* mPickMask; //downsampled bitmap approximation of alpha channel. NULL if no alpha channel
- U16 mPickMaskWidth;
- U16 mPickMaskHeight;
- S8 mUseMipMaps;
- bool mHasExplicitFormat; // If false (default), GL format is f(mComponents)
- bool mAutoGenMips = false;
-
- bool mIsMask;
- bool mNeedsAlphaAndPickMask;
- S8 mAlphaStride ;
- S8 mAlphaOffset ;
-
- bool mGLTextureCreated ;
- LLGLuint mTexName;
- //LLGLuint mNewTexName = 0; // tex name set by background thread to be applied in main thread
- U16 mWidth;
- U16 mHeight;
- S8 mCurrentDiscardLevel;
-
- S8 mDiscardLevelInAtlas;
- U32 mTexelsInAtlas ;
- U32 mTexelsInGLTexture;
-
- bool mAllowCompression;
-
-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;
- S32 mMipLevels;
-
- LLGLboolean mIsResident;
-
- S8 mComponents;
- 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)
-
- bool mExternalTexture;
-
- // STATICS
-public:
- static std::set<LLImageGL*> sImageList;
- static S32 sCount;
-
- static F32 sLastFrameTime;
-
- // Global memory statistics
- 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;
- static bool sCompressTextures; //use GL texture compression
-#if DEBUG_MISS
- bool mMissed; // Missed on last bind?
- bool getMissed() const { return mMissed; };
-#else
- bool getMissed() const { return false; };
-#endif
-
-public:
- static void initClass(LLWindow* window, S32 num_catagories, bool skip_analyze_alpha = false, bool thread_texture_loads = false, bool thread_media_updates = false);
- static void cleanupClass() ;
-
-private:
- static S32 sMaxCategories;
- static bool sSkipAnalyzeAlpha;
-
- //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) {mCategory = category;}
- S32 getCategory()const {return mCategory;}
-
- void setTexName(GLuint texName) { mTexName = texName; }
-
- //similar to setTexName, but will call deleteTextures on mTexName if mTexName is not 0 or texname
- void syncTexName(LLGLuint texname);
-
- //for debug use: show texture size distribution
- //----------------------------------------
- static S32 sCurTexSizeBar ;
- static S32 sCurTexPickSize ;
-
- static void setCurTexSizebar(S32 index, bool set_pick_size = true) ;
- static void resetCurTexSizebar();
-
-//****************************************************************************************************
-//End of definitions for texture auditing use only
-//****************************************************************************************************
-
-};
-
-class LLImageGLThread : public LLSimpleton<LLImageGLThread>, LL::ThreadPool
-{
-public:
- // follows gSavedSettings "RenderGLMultiThreadedTextures"
- static bool sEnabledTextures;
- // follows gSavedSettings "RenderGLMultiThreadedMedia"
- static bool sEnabledMedia;
-
- LLImageGLThread(LLWindow* window);
-
- // post a function to be executed on the LLImageGL background thread
- template <typename CALLABLE>
- bool post(CALLABLE&& func)
- {
- return getQueue().post(std::forward<CALLABLE>(func));
- }
-
- void run() override;
-
-private:
- LLWindow* mWindow;
- void* mContext = nullptr;
- LLAtomicBool mFinished;
-};
-
-#endif // LL_LLIMAGEGL_H
+/**
+ * @file llimagegl.h
+ * @brief Object for managing images and their textures
+ *
+ * $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_LLIMAGEGL_H
+#define LL_LLIMAGEGL_H
+
+#include "llimage.h"
+
+#include "llgltypes.h"
+#include "llpointer.h"
+#include "llrefcount.h"
+#include "v2math.h"
+#include "llunits.h"
+#include "llthreadsafequeue.h"
+#include "llrender.h"
+#include "threadpool.h"
+#include "workqueue.h"
+
+#define LL_IMAGEGL_THREAD_CHECK 0 //set to 1 to enable thread debugging for ImageGL
+
+class LLWindow;
+
+#define BYTES_TO_MEGA_BYTES(x) ((x) >> 20)
+#define MEGA_BYTES_TO_BYTES(x) ((x) << 20)
+
+namespace LLImageGLMemory
+{
+ void alloc_tex_image(U32 width, U32 height, U32 pixformat);
+ void free_tex_image(U32 texName);
+ void free_tex_images(U32 count, const U32* texNames);
+ void free_cur_tex_image();
+}
+
+//============================================================================
+class LLImageGL : public LLRefCount
+{
+ friend class LLTexUnit;
+public:
+
+ // Get an estimate of how many bytes have been allocated in vram for textures.
+ // Does not include mipmaps.
+ // NOTE: multiplying this number by two gives a good estimate for total
+ // video memory usage based on testing in lagland against an NVIDIA GPU.
+ static U64 getTextureBytesAllocated();
+
+ // These 2 functions replace glGenTextures() and glDeleteTextures()
+ static void generateTextures(S32 numTextures, U32 *textures);
+ static void deleteTextures(S32 numTextures, const U32 *textures);
+
+ // Size calculation
+ static S32 dataFormatBits(S32 dataformat);
+ static S64 dataFormatBytes(S32 dataformat, S32 width, S32 height);
+ static S32 dataFormatComponents(S32 dataformat);
+
+ bool updateBindStats() const ;
+ F32 getTimePassedSinceLastBound();
+ void forceUpdateBindStats(void) const;
+
+ // needs to be called every frame
+ static void updateStats(F32 current_time);
+
+ // Save off / restore GL textures
+ static void destroyGL(bool save_state = true);
+ static void restoreGL();
+ static void dirtyTexOptions();
+
+ 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);
+ LLImageGL(const LLImageRaw* imageraw, bool usemipmaps = true);
+
+ // For wrapping textures created via GL elsewhere with our API only. Use with caution.
+ LLImageGL(LLGLuint mTexName, U32 components, LLGLenum target, LLGLint formatInternal, LLGLenum formatPrimary, LLGLenum formatType, LLTexUnit::eTextureAddressMode addressMode);
+
+protected:
+ virtual ~LLImageGL();
+
+ void analyzeAlpha(const void* data_in, U32 w, U32 h);
+ void calcAlphaChannelOffsetAndStride();
+
+public:
+ virtual void dump(); // debugging info to LL_INFOS()
+
+ bool setSize(S32 width, S32 height, S32 ncomponents, S32 discard_level = -1);
+ void setComponents(S32 ncomponents) { mComponents = (S8)ncomponents ;}
+ void setAllowCompression(bool allow) { mAllowCompression = allow; }
+
+ static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression = true);
+
+ bool createGLTexture() ;
+ bool createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, bool to_create = true,
+ S32 category = sMaxCategories-1, bool defer_copy = false, LLGLuint* tex_name = nullptr);
+ bool createGLTexture(S32 discard_level, const U8* data, bool data_hasmips = false, S32 usename = 0, bool defer_copy = false, LLGLuint* tex_name = nullptr);
+ void setImage(const LLImageRaw* imageraw);
+ bool setImage(const U8* data_in, bool data_hasmips = false, S32 usename = 0);
+ // *TODO: This function may not work if the textures is compressed (i.e.
+ // RenderCompressTextures is 0). Partial image updates do not work on
+ // compressed textures.
+ bool setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, bool force_fast_update = false, LLGLuint use_name = 0);
+ 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, LLGLuint use_name = 0);
+ bool setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height);
+
+ // wait for gl commands to finish on current thread and push
+ // a lambda to main thread to swap mNewTexName and mTexName
+ void syncToMainThread(LLGLuint new_tex_name);
+
+ // Read back a raw image for this discard level, if it exists
+ bool readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const;
+ void destroyGLTexture();
+ void forceToInvalidateGLTexture();
+
+ void setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format = 0, bool swap_bytes = false);
+ 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; }
+ S64 getBytes(S32 discard_level = -1) const;
+ S64 getMipBytes(S32 discard_level = -1) const;
+ bool getBoundRecently() const;
+ bool isJustBound() const;
+ bool getHasExplicitFormat() const { return mHasExplicitFormat; }
+ LLGLenum getPrimaryFormat() const { return mFormatPrimary; }
+ LLGLenum getFormatType() const { return mFormatType; }
+
+ bool getHasGLTexture() const { return mTexName != 0; }
+ LLGLuint getTexName() const { return mTexName; }
+
+ bool getIsAlphaMask() const;
+
+ bool getIsResident(bool test_now = false); // not const
+
+ 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; }
+ void setHasMipMaps(bool hasmips) { mHasMipMaps = hasmips; }
+ void updatePickMask(S32 width, S32 height, const U8* data_in);
+ bool getMask(const LLVector2 &tc);
+
+ 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() ;
+
+#if LL_IMAGEGL_THREAD_CHECK
+ // thread debugging
+ std::thread::id mActiveThread;
+ void checkActiveThread();
+#endif
+
+public:
+ // Various GL/Rendering options
+ S64Bytes mTextureMemory;
+ mutable F32 mLastBindTime; // last time this was bound, by discard level
+
+private:
+ U32 createPickMask(S32 pWidth, S32 pHeight);
+ void freePickMask();
+ bool isCompressed();
+
+ LLPointer<LLImageRaw> mSaveData; // used for destroyGL/restoreGL
+ LL::WorkQueue::weak_t mMainQueue;
+ U8* mPickMask; //downsampled bitmap approximation of alpha channel. NULL if no alpha channel
+ U16 mPickMaskWidth;
+ U16 mPickMaskHeight;
+ S8 mUseMipMaps;
+ bool mHasExplicitFormat; // If false (default), GL format is f(mComponents)
+ bool mAutoGenMips = false;
+
+ bool mIsMask;
+ bool mNeedsAlphaAndPickMask;
+ S8 mAlphaStride ;
+ S8 mAlphaOffset ;
+
+ bool mGLTextureCreated ;
+ LLGLuint mTexName;
+ //LLGLuint mNewTexName = 0; // tex name set by background thread to be applied in main thread
+ U16 mWidth;
+ U16 mHeight;
+ S8 mCurrentDiscardLevel;
+
+ S8 mDiscardLevelInAtlas;
+ U32 mTexelsInAtlas ;
+ U32 mTexelsInGLTexture;
+
+ bool mAllowCompression;
+
+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;
+ S32 mMipLevels;
+
+ LLGLboolean mIsResident;
+
+ S8 mComponents;
+ 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)
+
+ bool mExternalTexture;
+
+ // STATICS
+public:
+ static std::set<LLImageGL*> sImageList;
+ static S32 sCount;
+
+ static F32 sLastFrameTime;
+
+ // Global memory statistics
+ 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;
+ static bool sCompressTextures; //use GL texture compression
+#if DEBUG_MISS
+ bool mMissed; // Missed on last bind?
+ bool getMissed() const { return mMissed; };
+#else
+ bool getMissed() const { return false; };
+#endif
+
+public:
+ static void initClass(LLWindow* window, S32 num_catagories, bool skip_analyze_alpha = false, bool thread_texture_loads = false, bool thread_media_updates = false);
+ static void cleanupClass() ;
+
+private:
+ static S32 sMaxCategories;
+ static bool sSkipAnalyzeAlpha;
+
+ //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) {mCategory = category;}
+ S32 getCategory()const {return mCategory;}
+
+ void setTexName(GLuint texName) { mTexName = texName; }
+
+ //similar to setTexName, but will call deleteTextures on mTexName if mTexName is not 0 or texname
+ void syncTexName(LLGLuint texname);
+
+ //for debug use: show texture size distribution
+ //----------------------------------------
+ static S32 sCurTexSizeBar ;
+ static S32 sCurTexPickSize ;
+
+ static void setCurTexSizebar(S32 index, bool set_pick_size = true) ;
+ static void resetCurTexSizebar();
+
+//****************************************************************************************************
+//End of definitions for texture auditing use only
+//****************************************************************************************************
+
+};
+
+class LLImageGLThread : public LLSimpleton<LLImageGLThread>, LL::ThreadPool
+{
+public:
+ // follows gSavedSettings "RenderGLMultiThreadedTextures"
+ static bool sEnabledTextures;
+ // follows gSavedSettings "RenderGLMultiThreadedMedia"
+ static bool sEnabledMedia;
+
+ LLImageGLThread(LLWindow* window);
+
+ // post a function to be executed on the LLImageGL background thread
+ template <typename CALLABLE>
+ bool post(CALLABLE&& func)
+ {
+ return getQueue().post(std::forward<CALLABLE>(func));
+ }
+
+ void run() override;
+
+private:
+ LLWindow* mWindow;
+ void* mContext = nullptr;
+ LLAtomicBool mFinished;
+};
+
+#endif // LL_LLIMAGEGL_H
diff --git a/indra/llrender/llpostprocess.cpp b/indra/llrender/llpostprocess.cpp
index f8409896a7..8ebd09f20d 100644
--- a/indra/llrender/llpostprocess.cpp
+++ b/indra/llrender/llpostprocess.cpp
@@ -1,454 +1,454 @@
-/**
- * @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"
-
-static LLStaticHashedString sRenderTexture("RenderTexture");
-static LLStaticHashedString sBrightness("brightness");
-static LLStaticHashedString sContrast("contrast");
-static LLStaticHashedString sContrastBase("contrastBase");
-static LLStaticHashedString sSaturation("saturation");
-static LLStaticHashedString sLumWeights("lumWeights");
-static LLStaticHashedString sNoiseTexture("NoiseTexture");
-static LLStaticHashedString sBrightMult("brightMult");
-static LLStaticHashedString sNoiseStrength("noiseStrength");
-static LLStaticHashedString sExtractLow("extractLow");
-static LLStaticHashedString sExtractHigh("extractHigh");
-static LLStaticHashedString sBloomStrength("bloomStrength");
-static LLStaticHashedString sTexelSize("texelSize");
-static LLStaticHashedString sBlurDirection("blurDirection");
-static LLStaticHashedString sBlurWidth("blurWidth");
-
-LLPostProcess * gPostProcess = NULL;
-
-static const unsigned int NOISE_SIZE = 512;
-
-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_DEBUGS("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));
- //LL_INFOS() << "Saving PostProcess Effects settings to " << pathName << LL_ENDL;
-
- 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)
-{
-
-}
-
-void LLPostProcess::createColorFilterShader(void)
-{
- /// Define uniform names
- colorFilterUniforms[sRenderTexture] = 0;
- colorFilterUniforms[sBrightness] = 0;
- colorFilterUniforms[sContrast] = 0;
- colorFilterUniforms[sContrastBase] = 0;
- colorFilterUniforms[sSaturation] = 0;
- colorFilterUniforms[sLumWeights] = 0;
-}
-
-void LLPostProcess::applyNightVisionShader(void)
-{
-
-}
-
-void LLPostProcess::createNightVisionShader(void)
-{
- /// Define uniform names
- nightVisionUniforms[sRenderTexture] = 0;
- nightVisionUniforms[sNoiseTexture] = 0;
- nightVisionUniforms[sBrightMult] = 0;
- nightVisionUniforms[sNoiseStrength] = 0;
- nightVisionUniforms[sLumWeights] = 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[sRenderTexture] = 0;
- bloomExtractUniforms[sExtractLow] = 0;
- bloomExtractUniforms[sExtractHigh] = 0;
- bloomExtractUniforms[sLumWeights] = 0;
-
- /// Create Bloom Blur Shader
- bloomBlurUniforms[sRenderTexture] = 0;
- bloomBlurUniforms[sBloomStrength] = 0;
- bloomBlurUniforms[sTexelSize] = 0;
- bloomBlurUniforms[sBlurDirection] = 0;
- bloomBlurUniforms[sBlurWidth] = 0;
-}
-
-void LLPostProcess::getShaderUniforms(glslUniforms & uniforms, GLuint & prog)
-{
- /// Find uniform locations and insert into map
- glslUniforms::iterator i;
- for (i = uniforms.begin(); i != uniforms.end(); ++i){
- i->second = glGetUniformLocation(prog, i->first.String().c_str());
- }
-}
-
-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::unbind();
- 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_TEXTURE, texture);
- glCopyTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, 0, 0, width, height, 0);
-}
-
-void LLPostProcess::drawOrthoQuad(unsigned int width, unsigned int height, QuadType type)
-{
-
-}
-
-void LLPostProcess::viewOrthogonal(unsigned int width, unsigned int height)
-{
- gGL.matrixMode(LLRender::MM_PROJECTION);
- gGL.pushMatrix();
- gGL.loadIdentity();
- gGL.ortho( 0.f, (GLdouble) width , (GLdouble) height , 0.f, -1.f, 1.f );
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- gGL.pushMatrix();
- gGL.loadIdentity();
-}
-
-void LLPostProcess::viewPerspective(void)
-{
- gGL.matrixMode(LLRender::MM_PROJECTION);
- gGL.popMatrix();
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- gGL.popMatrix();
-}
-
-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_TEXTURE, texture->getTexName());
- glTexImage2D(GL_TEXTURE_RECTANGLE, 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(GLuint shader)
-{
- GLint infologLength = 0;
- GLint charsWritten = 0;
- GLchar *infoLog;
-
- checkError(); // Check for OpenGL errors
-
- glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologLength);
-
- checkError(); // Check for OpenGL errors
-
- if (infologLength > 0)
- {
- infoLog = (GLchar *)malloc(infologLength);
- if (infoLog == NULL)
- {
- /// Could not allocate infolog buffer
- return;
- }
- glGetProgramInfoLog(shader, infologLength, &charsWritten, infoLog);
- // shaderErrorLog << (char *) infoLog << std::endl;
- mShaderErrorString = (char *) infoLog;
- free(infoLog);
- }
- checkError(); // Check for OpenGL errors
-}
+/**
+ * @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"
+
+static LLStaticHashedString sRenderTexture("RenderTexture");
+static LLStaticHashedString sBrightness("brightness");
+static LLStaticHashedString sContrast("contrast");
+static LLStaticHashedString sContrastBase("contrastBase");
+static LLStaticHashedString sSaturation("saturation");
+static LLStaticHashedString sLumWeights("lumWeights");
+static LLStaticHashedString sNoiseTexture("NoiseTexture");
+static LLStaticHashedString sBrightMult("brightMult");
+static LLStaticHashedString sNoiseStrength("noiseStrength");
+static LLStaticHashedString sExtractLow("extractLow");
+static LLStaticHashedString sExtractHigh("extractHigh");
+static LLStaticHashedString sBloomStrength("bloomStrength");
+static LLStaticHashedString sTexelSize("texelSize");
+static LLStaticHashedString sBlurDirection("blurDirection");
+static LLStaticHashedString sBlurWidth("blurWidth");
+
+LLPostProcess * gPostProcess = NULL;
+
+static const unsigned int NOISE_SIZE = 512;
+
+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_DEBUGS("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));
+ //LL_INFOS() << "Saving PostProcess Effects settings to " << pathName << LL_ENDL;
+
+ 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)
+{
+
+}
+
+void LLPostProcess::createColorFilterShader(void)
+{
+ /// Define uniform names
+ colorFilterUniforms[sRenderTexture] = 0;
+ colorFilterUniforms[sBrightness] = 0;
+ colorFilterUniforms[sContrast] = 0;
+ colorFilterUniforms[sContrastBase] = 0;
+ colorFilterUniforms[sSaturation] = 0;
+ colorFilterUniforms[sLumWeights] = 0;
+}
+
+void LLPostProcess::applyNightVisionShader(void)
+{
+
+}
+
+void LLPostProcess::createNightVisionShader(void)
+{
+ /// Define uniform names
+ nightVisionUniforms[sRenderTexture] = 0;
+ nightVisionUniforms[sNoiseTexture] = 0;
+ nightVisionUniforms[sBrightMult] = 0;
+ nightVisionUniforms[sNoiseStrength] = 0;
+ nightVisionUniforms[sLumWeights] = 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[sRenderTexture] = 0;
+ bloomExtractUniforms[sExtractLow] = 0;
+ bloomExtractUniforms[sExtractHigh] = 0;
+ bloomExtractUniforms[sLumWeights] = 0;
+
+ /// Create Bloom Blur Shader
+ bloomBlurUniforms[sRenderTexture] = 0;
+ bloomBlurUniforms[sBloomStrength] = 0;
+ bloomBlurUniforms[sTexelSize] = 0;
+ bloomBlurUniforms[sBlurDirection] = 0;
+ bloomBlurUniforms[sBlurWidth] = 0;
+}
+
+void LLPostProcess::getShaderUniforms(glslUniforms & uniforms, GLuint & prog)
+{
+ /// Find uniform locations and insert into map
+ glslUniforms::iterator i;
+ for (i = uniforms.begin(); i != uniforms.end(); ++i){
+ i->second = glGetUniformLocation(prog, i->first.String().c_str());
+ }
+}
+
+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::unbind();
+ 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_TEXTURE, texture);
+ glCopyTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, 0, 0, width, height, 0);
+}
+
+void LLPostProcess::drawOrthoQuad(unsigned int width, unsigned int height, QuadType type)
+{
+
+}
+
+void LLPostProcess::viewOrthogonal(unsigned int width, unsigned int height)
+{
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.pushMatrix();
+ gGL.loadIdentity();
+ gGL.ortho( 0.f, (GLdouble) width , (GLdouble) height , 0.f, -1.f, 1.f );
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ gGL.pushMatrix();
+ gGL.loadIdentity();
+}
+
+void LLPostProcess::viewPerspective(void)
+{
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.popMatrix();
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ gGL.popMatrix();
+}
+
+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_TEXTURE, texture->getTexName());
+ glTexImage2D(GL_TEXTURE_RECTANGLE, 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(GLuint shader)
+{
+ GLint infologLength = 0;
+ GLint charsWritten = 0;
+ GLchar *infoLog;
+
+ checkError(); // Check for OpenGL errors
+
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologLength);
+
+ checkError(); // Check for OpenGL errors
+
+ if (infologLength > 0)
+ {
+ infoLog = (GLchar *)malloc(infologLength);
+ if (infoLog == NULL)
+ {
+ /// Could not allocate infolog buffer
+ return;
+ }
+ glGetProgramInfoLog(shader, infologLength, &charsWritten, infoLog);
+ // shaderErrorLog << (char *) infoLog << std::endl;
+ mShaderErrorString = (char *) infoLog;
+ free(infoLog);
+ }
+ checkError(); // Check for OpenGL errors
+}
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index d278f4e55d..7ad03a32a8 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -1,2266 +1,2266 @@
- /**
- * @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 "llglslshader.h"
-#include "llimagegl.h"
-#include "llrendertarget.h"
-#include "lltexture.h"
-#include "llshadermgr.h"
-#include "hbxxh.h"
-
-#if LL_WINDOWS
-extern void APIENTRY gl_debug_callback(GLenum source,
- GLenum type,
- GLuint id,
- GLenum severity,
- GLsizei length,
- const GLchar* message,
- GLvoid* userParam)
-;
-#endif
-
-thread_local LLRender gGL;
-
-// Handy copies of last good GL matrices
-F32 gGLModelView[16];
-F32 gGLLastModelView[16];
-F32 gGLLastProjection[16];
-F32 gGLProjection[16];
-
-// transform from last frame's camera space to this frame's camera space (and inverse)
-F32 gGLDeltaModelView[16];
-F32 gGLInverseDeltaModelView[16];
-
-S32 gGLViewport[4];
-
-
-U32 LLRender::sUICalls = 0;
-U32 LLRender::sUIVerts = 0;
-U32 LLTexUnit::sWhiteTexture = 0;
-bool LLRender::sGLCoreProfile = false;
-bool LLRender::sNsightDebugSupport = false;
-LLVector2 LLRender::sUIGLScaleFactor = LLVector2(1.f, 1.f);
-
-struct LLVBCache
-{
- LLPointer<LLVertexBuffer> vb;
- std::chrono::steady_clock::time_point touched;
-};
-
-static std::unordered_map<U64, LLVBCache> sVBCache;
-
-static const GLenum sGLTextureType[] =
-{
- GL_TEXTURE_2D,
- GL_TEXTURE_RECTANGLE,
- GL_TEXTURE_CUBE_MAP,
- GL_TEXTURE_CUBE_MAP_ARRAY,
- GL_TEXTURE_2D_MULTISAMPLE,
- GL_TEXTURE_3D
-};
-
-static const GLint sGLAddressMode[] =
-{
- GL_REPEAT,
- GL_MIRRORED_REPEAT,
- GL_CLAMP_TO_EDGE
-};
-
-const U32 immediate_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_TEXCOORD0;
-
-static const 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),
- mCurrColorScale(1), mCurrAlphaScale(1), mCurrTexture(0), mTexColorSpace(TCS_LINEAR),
- mHasMipMaps(false),
- mIndex(index)
-{
- llassert_always(index < (S32)LL_NUM_TEXTURE_LAYERS);
-}
-
-//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();
-
- glActiveTexture(GL_TEXTURE0 + mIndex);
-
- if (mCurrTexType != TT_NONE)
- {
- glBindTexture(sGLTextureType[mCurrTexType], mCurrTexture);
- }
- else
- {
- glBindTexture(GL_TEXTURE_2D, 0);
- }
-
- setTextureColorSpace(mTexColorSpace);
-}
-
-void LLTexUnit::activate(void)
-{
- if (mIndex < 0) return;
-
- if ((S32)gGL.mCurrTextureUnitIndex != mIndex || gGL.mDirty)
- {
- gGL.flush();
- glActiveTexture(GL_TEXTURE0 + 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();
- }
-}
-
-void LLTexUnit::disable(void)
-{
- if (mIndex < 0) return;
-
- if (mCurrTexType != TT_NONE)
- {
- unbind(mCurrTexType);
- mCurrTexType = TT_NONE;
- }
-}
-
-void LLTexUnit::bindFast(LLTexture* texture)
-{
- LLImageGL* gl_tex = texture->getGLTexture();
- texture->setActive();
- glActiveTexture(GL_TEXTURE0 + mIndex);
- gGL.mCurrTextureUnitIndex = mIndex;
- mCurrTexture = gl_tex->getTexName();
- if (!mCurrTexture)
- {
- LL_PROFILE_ZONE_NAMED("MISSING TEXTURE");
- //if deleted, will re-generate it immediately
- texture->forceImmediateUpdate();
- gl_tex->forceUpdateBindStats();
- texture->bindDefaultImage(mIndex);
- }
- glBindTexture(sGLTextureType[gl_tex->getTarget()], mCurrTexture);
- mHasMipMaps = gl_tex->mHasMipMaps;
-}
-
-bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE;
- stop_glerror();
- if (mIndex >= 0)
- {
- gGL.flush();
-
- LLImageGL* gl_tex = NULL ;
-
- if (texture != NULL && (gl_tex = texture->getGLTexture()))
- {
- if (gl_tex->getTexName()) //if texture exists
- {
- //in audit, replace the selected texture by the default one.
- 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())
- {
- texture->setActive() ;
- texture->updateBindStatsForTester() ;
- }
- mHasMipMaps = gl_tex->mHasMipMaps;
- if (gl_tex->mTexOptionsDirty)
- {
- gl_tex->mTexOptionsDirty = false;
- setTextureAddressMode(gl_tex->mAddressMode);
- setTextureFilteringOption(gl_tex->mFilterOption);
- }
- setTextureColorSpace(mTexColorSpace);
- }
- }
- else
- {
- //if deleted, will re-generate it immediately
- texture->forceImmediateUpdate() ;
-
- gl_tex->forceUpdateBindStats() ;
- return texture->bindDefaultImage(mIndex);
- }
- }
- else
- {
- if (texture)
- {
- LL_DEBUGS() << "NULL LLTexUnit::bind GL image" << LL_ENDL;
- }
- else
- {
- LL_DEBUGS() << "NULL LLTexUnit::bind texture" << LL_ENDL;
- }
- return false;
- }
- }
- else
- { // mIndex < 0
- return false;
- }
-
- return true;
-}
-
-bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind, S32 usename)
-{
- stop_glerror();
- if (mIndex < 0) return false;
-
- U32 texname = usename ? usename : texture->getTexName();
-
- if(!texture)
- {
- LL_DEBUGS() << "NULL LLTexUnit::bind texture" << LL_ENDL;
- return false;
- }
-
- if(!texname)
- {
- if(LLImageGL::sDefaultGLTexture && LLImageGL::sDefaultGLTexture->getTexName())
- {
- return bind(LLImageGL::sDefaultGLTexture) ;
- }
- stop_glerror();
- return false ;
- }
-
- if ((mCurrTexture != texname) || forceBind)
- {
- gGL.flush();
- stop_glerror();
- activate();
- stop_glerror();
- enable(texture->getTarget());
- stop_glerror();
- mCurrTexture = texname;
- glBindTexture(sGLTextureType[texture->getTarget()], mCurrTexture);
- stop_glerror();
- texture->updateBindStats();
- mHasMipMaps = texture->mHasMipMaps;
- if (texture->mTexOptionsDirty)
- {
- stop_glerror();
- texture->mTexOptionsDirty = false;
- setTextureAddressMode(texture->mAddressMode);
- setTextureFilteringOption(texture->mFilterOption);
- stop_glerror();
- }
- setTextureColorSpace(mTexColorSpace);
- }
-
- stop_glerror();
-
- return true;
-}
-
-bool LLTexUnit::bind(LLCubeMap* cubeMap)
-{
- if (mIndex < 0) return false;
-
- gGL.flush();
-
- if (cubeMap == NULL)
- {
- LL_WARNS() << "NULL LLTexUnit::bind cubemap" << LL_ENDL;
- return false;
- }
-
- if (mCurrTexture != cubeMap->mImages[0]->getTexName())
- {
- if (LLCubeMap::sUseCubeMaps)
- {
- activate();
- enable(LLTexUnit::TT_CUBE_MAP);
- mCurrTexture = cubeMap->mImages[0]->getTexName();
- glBindTexture(GL_TEXTURE_CUBE_MAP, mCurrTexture);
- mHasMipMaps = cubeMap->mImages[0]->mHasMipMaps;
- cubeMap->mImages[0]->updateBindStats();
- if (cubeMap->mImages[0]->mTexOptionsDirty)
- {
- cubeMap->mImages[0]->mTexOptionsDirty = false;
- setTextureAddressMode(cubeMap->mImages[0]->mAddressMode);
- setTextureFilteringOption(cubeMap->mImages[0]->mFilterOption);
- }
- setTextureColorSpace(mTexColorSpace);
- return true;
- }
- else
- {
- LL_WARNS() << "Using cube map without extension!" << LL_ENDL;
- return false;
- }
- }
- return true;
-}
-
-// LLRenderTarget is unavailible on the mapserver since it uses FBOs.
-bool LLTexUnit::bind(LLRenderTarget* renderTarget, bool bindDepth)
-{
- if (mIndex < 0) return false;
-
- gGL.flush();
-
- if (bindDepth)
- {
- llassert(renderTarget->getDepth()); // target MUST have a depth buffer attachment
-
- bindManual(renderTarget->getUsage(), renderTarget->getDepth());
- }
- else
- {
- bindManual(renderTarget->getUsage(), renderTarget->getTexture());
- }
-
- return true;
-}
-
-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;
- setTextureColorSpace(mTexColorSpace);
- }
- return true;
-}
-
-void LLTexUnit::unbind(eTextureType type)
-{
- stop_glerror();
-
- if (mIndex < 0) return;
-
- //always flush and activate for consistency
- // some code paths assume unbind always flushes and sets the active texture
- gGL.flush();
- activate();
-
- // Disabled caching of binding state.
- if (mCurrTexType == type)
- {
- mCurrTexture = 0;
-
- // Always make sure our texture color space is reset to linear. SRGB sampling should be opt-in in the vast majority of cases. Also prevents color space "popping".
- mTexColorSpace = TCS_LINEAR;
- if (type == LLTexUnit::TT_TEXTURE)
- {
- glBindTexture(sGLTextureType[type], sWhiteTexture);
- }
- else
- {
- glBindTexture(sGLTextureType[type], 0);
- }
- stop_glerror();
- }
-}
-
-void LLTexUnit::unbindFast(eTextureType type)
-{
- activate();
-
- // Disabled caching of binding state.
- if (mCurrTexType == type)
- {
- mCurrTexture = 0;
-
- // Always make sure our texture color space is reset to linear. SRGB sampling should be opt-in in the vast majority of cases. Also prevents color space "popping".
- mTexColorSpace = TCS_LINEAR;
- if (type == LLTexUnit::TT_TEXTURE)
- {
- glBindTexture(sGLTextureType[type], sWhiteTexture);
- }
- else
- {
- 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, GL_TEXTURE_WRAP_R, sGLAddressMode[mode]);
- }
-}
-
-void LLTexUnit::setTextureFilteringOption(LLTexUnit::eTextureFilterOptions option)
-{
- if (mIndex < 0 || mCurrTexture == 0 || mCurrTexType == LLTexUnit::TT_MULTISAMPLE_TEXTURE) 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)
- {
- if (mHasMipMaps)
- {
- glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
- }
- else
- {
- glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- }
- }
- else
- {
- if (mHasMipMaps)
- {
- glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
- }
- else
- {
- glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- }
- }
-
- if (gGLManager.mGLVersion >= 4.59f)
- {
- if (LLImageGL::sGlobalUseAnisotropic && option == TFO_ANISOTROPIC)
- {
- glTexParameterf(sGLTextureType[mCurrTexType], GL_TEXTURE_MAX_ANISOTROPY, gGLManager.mMaxAnisotropy);
- }
- else
- {
- glTexParameterf(sGLTextureType[mCurrTexType], GL_TEXTURE_MAX_ANISOTROPY, 1.f);
- }
- }
-}
-
-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;
-
- // 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;
-
- // 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;
-
- default:
- LL_WARNS() << "Unknown eTextureBlendSrc: " << src << ". Using Vertex Color instead." << LL_ENDL;
- return GL_PRIMARY_COLOR;
- }
-}
-
-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:
- LL_WARNS() << "Unknown eTextureBlendSrc: " << src << ". Using Source Color or Alpha instead." << LL_ENDL;
- return (isAlpha) ? GL_SRC_ALPHA: GL_SRC_COLOR;
- }
-}
-
-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, &activeTexture);
- if ((GL_TEXTURE0 + mIndex) != activeTexture)
- {
- U32 set_unit = (activeTexture - GL_TEXTURE0);
- LL_WARNS() << "Incorrect Texture Unit! Expected: " << set_unit << " Actual: " << mIndex << LL_ENDL;
- }
-}
-
-void LLTexUnit::setTextureColorSpace(eTextureColorSpace space)
-{
- mTexColorSpace = space;
-}
-
-LLLightState::LLLightState(S32 index)
-: mIndex(index),
- mEnabled(false),
- mConstantAtten(1.f),
- mLinearAtten(0.f),
- mQuadraticAtten(0.f),
- mSpotExponent(0.f),
- mSpotCutoff(180.f)
-{
- if (mIndex == 0)
- {
- mDiffuse.set(1,1,1,1);
- mDiffuseB.set(0,0,0,0);
- mSpecular.set(1,1,1,1);
- }
-
- mSunIsPrimary = true;
-
- mAmbient.set(0,0,0,1);
- mPosition.set(0,0,1,0);
- mSpotDirection.set(0,0,-1);
-}
-
-void LLLightState::enable()
-{
- mEnabled = true;
-}
-
-void LLLightState::disable()
-{
- mEnabled = false;
-}
-
-void LLLightState::setDiffuse(const LLColor4& diffuse)
-{
- if (mDiffuse != diffuse)
- {
- ++gGL.mLightHash;
- mDiffuse = diffuse;
- }
-}
-
-void LLLightState::setDiffuseB(const LLColor4& diffuse)
-{
- if (mDiffuseB != diffuse)
- {
- ++gGL.mLightHash;
- mDiffuseB = diffuse;
- }
-}
-
-void LLLightState::setSunPrimary(bool v)
-{
- if (mSunIsPrimary != v)
- {
- ++gGL.mLightHash;
- mSunIsPrimary = v;
- }
-}
-
-void LLLightState::setSize(F32 v)
-{
- if (mSize != v)
- {
- ++gGL.mLightHash;
- mSize = v;
- }
-}
-
-void LLLightState::setFalloff(F32 v)
-{
- if (mFalloff != v)
- {
- ++gGL.mLightHash;
- mFalloff = v;
- }
-}
-
-void LLLightState::setAmbient(const LLColor4& ambient)
-{
- if (mAmbient != ambient)
- {
- ++gGL.mLightHash;
- mAmbient = ambient;
- }
-}
-
-void LLLightState::setSpecular(const LLColor4& specular)
-{
- if (mSpecular != specular)
- {
- ++gGL.mLightHash;
- mSpecular = specular;
- }
-}
-
-void LLLightState::setPosition(const LLVector4& position)
-{
- //always set position because modelview matrix may have changed
- ++gGL.mLightHash;
- mPosition = position;
- //transform position by current modelview matrix
- glh::vec4f pos(position.mV);
- const glh::matrix4f& mat = gGL.getModelviewMatrix();
- mat.mult_matrix_vec(pos);
- mPosition.set(pos.v);
-}
-
-void LLLightState::setConstantAttenuation(const F32& atten)
-{
- if (mConstantAtten != atten)
- {
- mConstantAtten = atten;
- ++gGL.mLightHash;
- }
-}
-
-void LLLightState::setLinearAttenuation(const F32& atten)
-{
- if (mLinearAtten != atten)
- {
- ++gGL.mLightHash;
- mLinearAtten = atten;
- }
-}
-
-void LLLightState::setQuadraticAttenuation(const F32& atten)
-{
- if (mQuadraticAtten != atten)
- {
- ++gGL.mLightHash;
- mQuadraticAtten = atten;
- }
-}
-
-void LLLightState::setSpotExponent(const F32& exponent)
-{
- if (mSpotExponent != exponent)
- {
- ++gGL.mLightHash;
- mSpotExponent = exponent;
- }
-}
-
-void LLLightState::setSpotCutoff(const F32& cutoff)
-{
- if (mSpotCutoff != cutoff)
- {
- ++gGL.mLightHash;
- mSpotCutoff = cutoff;
- }
-}
-
-void LLLightState::setSpotDirection(const LLVector3& direction)
-{
- //always set direction because modelview matrix may have changed
- ++gGL.mLightHash;
- mSpotDirection = direction;
- //transform direction by current modelview matrix
- glh::vec3f dir(direction.mV);
- const glh::matrix4f& mat = gGL.getModelviewMatrix();
- mat.mult_matrix_dir(dir);
-
- mSpotDirection.set(dir.v);
-}
-
-LLRender::LLRender()
- : mDirty(false),
- mCount(0),
- mQuadCycle(0),
- mMode(LLRender::TRIANGLES),
- mCurrTextureUnitIndex(0)
-{
- for (U32 i = 0; i < LL_NUM_TEXTURE_LAYERS; i++)
- {
- mTexUnits[i].mIndex = i;
- }
-
- for (U32 i = 0; i < LL_NUM_LIGHT_UNITS; ++i)
- {
- mLightState[i].mIndex = i;
- }
-
- for (U32 i = 0; i < 4; i++)
- {
- mCurrColorMask[i] = true;
- }
-
- mCurrBlendColorSFactor = BF_UNDEF;
- mCurrBlendAlphaSFactor = BF_UNDEF;
- mCurrBlendColorDFactor = BF_UNDEF;
- mCurrBlendAlphaDFactor = BF_UNDEF;
-
- mMatrixMode = LLRender::MM_MODELVIEW;
-
- for (U32 i = 0; i < NUM_MATRIX_MODES; ++i)
- {
- mMatIdx[i] = 0;
- mMatHash[i] = 0;
- mCurMatHash[i] = 0xFFFFFFFF;
- }
-
- mLightHash = 0;
-}
-
-LLRender::~LLRender()
-{
- shutdown();
-}
-
-bool LLRender::init(bool needs_vertex_buffer)
-{
-#if LL_WINDOWS
- if (gGLManager.mHasDebugOutput && gDebugGL)
- { //setup debug output callback
- //glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW_ARB, 0, NULL, GL_TRUE);
- glDebugMessageCallback((GLDEBUGPROC) gl_debug_callback, NULL);
- glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
- }
-#endif
-
- glPixelStorei(GL_PACK_ALIGNMENT, 1);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
- gGL.setAmbientLightColor(LLColor4::black);
-
- glCullFace(GL_BACK);
-
- // necessary for reflection maps
- glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
-
-#if LL_WINDOWS
- if (glGenVertexArrays == nullptr)
- {
- return false;
- }
-#endif
-
- { //bind a dummy vertex array object so we're core profile compliant
- U32 ret;
- glGenVertexArrays(1, &ret);
- glBindVertexArray(ret);
- }
-
- if (needs_vertex_buffer)
- {
- initVertexBuffer();
- }
- return true;
-}
-
-void LLRender::initVertexBuffer()
-{
- llassert_always(mBuffer.isNull());
- stop_glerror();
- mBuffer = new LLVertexBuffer(immediate_mask);
- mBuffer->allocateBuffer(4096, 0);
- mBuffer->getVertexStrider(mVerticesp);
- mBuffer->getTexCoord0Strider(mTexcoordsp);
- mBuffer->getColorStrider(mColorsp);
- stop_glerror();
-}
-
-void LLRender::resetVertexBuffer()
-{
- mBuffer = NULL;
-}
-
-void LLRender::shutdown()
-{
- resetVertexBuffer();
-}
-
-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]);
-
- flush();
-
- mDirty = false;
-}
-
-void LLRender::syncLightState()
-{
- LLGLSLShader *shader = LLGLSLShader::sCurBoundShaderPtr;
-
- if (!shader)
- {
- return;
- }
-
- if (shader->mLightHash != mLightHash)
- {
- shader->mLightHash = mLightHash;
-
- LLVector4 position[LL_NUM_LIGHT_UNITS];
- LLVector3 direction[LL_NUM_LIGHT_UNITS];
- LLVector4 attenuation[LL_NUM_LIGHT_UNITS];
- LLVector3 diffuse[LL_NUM_LIGHT_UNITS];
- LLVector3 diffuse_b[LL_NUM_LIGHT_UNITS];
- bool sun_primary[LL_NUM_LIGHT_UNITS];
- LLVector2 size[LL_NUM_LIGHT_UNITS];
-
- for (U32 i = 0; i < LL_NUM_LIGHT_UNITS; i++)
- {
- LLLightState *light = &mLightState[i];
-
- position[i] = light->mPosition;
- direction[i] = light->mSpotDirection;
- attenuation[i].set(light->mLinearAtten, light->mQuadraticAtten, light->mSpecular.mV[2], light->mSpecular.mV[3]);
- diffuse[i].set(light->mDiffuse.mV);
- diffuse_b[i].set(light->mDiffuseB.mV);
- sun_primary[i] = light->mSunIsPrimary;
- size[i].set(light->mSize, light->mFalloff);
- }
-
- shader->uniform4fv(LLShaderMgr::LIGHT_POSITION, LL_NUM_LIGHT_UNITS, position[0].mV);
- shader->uniform3fv(LLShaderMgr::LIGHT_DIRECTION, LL_NUM_LIGHT_UNITS, direction[0].mV);
- shader->uniform4fv(LLShaderMgr::LIGHT_ATTENUATION, LL_NUM_LIGHT_UNITS, attenuation[0].mV);
- shader->uniform2fv(LLShaderMgr::LIGHT_DEFERRED_ATTENUATION, LL_NUM_LIGHT_UNITS, size[0].mV);
- shader->uniform3fv(LLShaderMgr::LIGHT_DIFFUSE, LL_NUM_LIGHT_UNITS, diffuse[0].mV);
- shader->uniform3fv(LLShaderMgr::LIGHT_AMBIENT, 1, mAmbientLightColor.mV);
- shader->uniform1i(LLShaderMgr::SUN_UP_FACTOR, sun_primary[0] ? 1 : 0);
- //shader->uniform3fv(LLShaderMgr::AMBIENT, 1, mAmbientLightColor.mV);
- //shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, 1, diffuse[0].mV);
- //shader->uniform3fv(LLShaderMgr::MOONLIGHT_COLOR, 1, diffuse_b[0].mV);
- }
-}
-
-void LLRender::syncMatrices()
-{
- static const U32 name[] =
- {
- LLShaderMgr::MODELVIEW_MATRIX,
- LLShaderMgr::PROJECTION_MATRIX,
- LLShaderMgr::TEXTURE_MATRIX0,
- LLShaderMgr::TEXTURE_MATRIX1,
- LLShaderMgr::TEXTURE_MATRIX2,
- LLShaderMgr::TEXTURE_MATRIX3,
- };
-
- LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
-
- static glh::matrix4f cached_mvp;
- static glh::matrix4f cached_inv_mdv;
- static U32 cached_mvp_mdv_hash = 0xFFFFFFFF;
- static U32 cached_mvp_proj_hash = 0xFFFFFFFF;
-
- static glh::matrix4f cached_normal;
- static U32 cached_normal_hash = 0xFFFFFFFF;
-
- if (shader)
- {
- //llassert(shader);
-
- bool mvp_done = false;
-
- U32 i = MM_MODELVIEW;
- if (mMatHash[MM_MODELVIEW] != shader->mMatHash[MM_MODELVIEW])
- { //update modelview, normal, and MVP
- glh::matrix4f& mat = mMatrix[MM_MODELVIEW][mMatIdx[MM_MODELVIEW]];
-
- // if MDV has changed, update the cached inverse as well
- if (cached_mvp_mdv_hash != mMatHash[MM_MODELVIEW])
- {
- cached_inv_mdv = mat.inverse();
- }
-
- shader->uniformMatrix4fv(name[MM_MODELVIEW], 1, GL_FALSE, mat.m);
- shader->mMatHash[MM_MODELVIEW] = mMatHash[MM_MODELVIEW];
-
- //update normal matrix
- S32 loc = shader->getUniformLocation(LLShaderMgr::NORMAL_MATRIX);
- if (loc > -1)
- {
- if (cached_normal_hash != mMatHash[i])
- {
- cached_normal = cached_inv_mdv.transpose();
- cached_normal_hash = mMatHash[i];
- }
-
- glh::matrix4f& norm = cached_normal;
-
- F32 norm_mat[] =
- {
- norm.m[0], norm.m[1], norm.m[2],
- norm.m[4], norm.m[5], norm.m[6],
- norm.m[8], norm.m[9], norm.m[10]
- };
-
- shader->uniformMatrix3fv(LLShaderMgr::NORMAL_MATRIX, 1, GL_FALSE, norm_mat);
- }
-
- if (shader->getUniformLocation(LLShaderMgr::INVERSE_MODELVIEW_MATRIX))
- {
- shader->uniformMatrix4fv(LLShaderMgr::INVERSE_MODELVIEW_MATRIX, 1, GL_FALSE, cached_inv_mdv.m);
- }
-
- //update MVP matrix
- mvp_done = true;
- loc = shader->getUniformLocation(LLShaderMgr::MODELVIEW_PROJECTION_MATRIX);
- if (loc > -1)
- {
- U32 proj = MM_PROJECTION;
-
- if (cached_mvp_mdv_hash != mMatHash[i] || cached_mvp_proj_hash != mMatHash[MM_PROJECTION])
- {
- cached_mvp = mat;
- cached_mvp.mult_left(mMatrix[proj][mMatIdx[proj]]);
- cached_mvp_mdv_hash = mMatHash[i];
- cached_mvp_proj_hash = mMatHash[MM_PROJECTION];
- }
-
- shader->uniformMatrix4fv(LLShaderMgr::MODELVIEW_PROJECTION_MATRIX, 1, GL_FALSE, cached_mvp.m);
- }
- }
-
- i = MM_PROJECTION;
- if (mMatHash[MM_PROJECTION] != shader->mMatHash[MM_PROJECTION])
- { //update projection matrix, normal, and MVP
- glh::matrix4f& mat = mMatrix[MM_PROJECTION][mMatIdx[MM_PROJECTION]];
-
- // GZ: This was previously disabled seemingly due to a bug involving the deferred renderer's regular pushing and popping of mats.
- // We're reenabling this and cleaning up the code around that - that would've been the appropriate course initially.
- // Anything beyond the standard proj and inv proj mats are special cases. Please setup special uniforms accordingly in the future.
- if (shader->getUniformLocation(LLShaderMgr::INVERSE_PROJECTION_MATRIX))
- {
- glh::matrix4f inv_proj = mat.inverse();
- shader->uniformMatrix4fv(LLShaderMgr::INVERSE_PROJECTION_MATRIX, 1, false, inv_proj.m);
- }
-
- // Used by some full screen effects - such as full screen lights, glow, etc.
- if (shader->getUniformLocation(LLShaderMgr::IDENTITY_MATRIX))
- {
- shader->uniformMatrix4fv(LLShaderMgr::IDENTITY_MATRIX, 1, GL_FALSE, glh::matrix4f::identity().m);
- }
-
- shader->uniformMatrix4fv(name[MM_PROJECTION], 1, GL_FALSE, mat.m);
- shader->mMatHash[MM_PROJECTION] = mMatHash[MM_PROJECTION];
-
- if (!mvp_done)
- {
- //update MVP matrix
- S32 loc = shader->getUniformLocation(LLShaderMgr::MODELVIEW_PROJECTION_MATRIX);
- if (loc > -1)
- {
- if (cached_mvp_mdv_hash != mMatHash[MM_PROJECTION] || cached_mvp_proj_hash != mMatHash[MM_PROJECTION])
- {
- U32 mdv = MM_MODELVIEW;
- cached_mvp = mat;
- cached_mvp.mult_right(mMatrix[mdv][mMatIdx[mdv]]);
- cached_mvp_mdv_hash = mMatHash[MM_MODELVIEW];
- cached_mvp_proj_hash = mMatHash[MM_PROJECTION];
- }
-
- shader->uniformMatrix4fv(LLShaderMgr::MODELVIEW_PROJECTION_MATRIX, 1, GL_FALSE, cached_mvp.m);
- }
- }
- }
-
- for (i = MM_TEXTURE0; i < NUM_MATRIX_MODES; ++i)
- {
- if (mMatHash[i] != shader->mMatHash[i])
- {
- shader->uniformMatrix4fv(name[i], 1, GL_FALSE, mMatrix[i][mMatIdx[i]].m);
- shader->mMatHash[i] = mMatHash[i];
- }
- }
-
-
- if (shader->mFeatures.hasLighting || shader->mFeatures.calculatesLighting || shader->mFeatures.calculatesAtmospherics)
- { //also sync light state
- syncLightState();
- }
- }
-}
-
-void LLRender::translatef(const GLfloat& x, const GLfloat& y, const GLfloat& z)
-{
- flush();
-
- {
- glh::matrix4f trans_mat(1,0,0,x,
- 0,1,0,y,
- 0,0,1,z,
- 0,0,0,1);
-
- mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].mult_right(trans_mat);
- mMatHash[mMatrixMode]++;
- }
-}
-
-void LLRender::scalef(const GLfloat& x, const GLfloat& y, const GLfloat& z)
-{
- flush();
-
- {
- glh::matrix4f scale_mat(x,0,0,0,
- 0,y,0,0,
- 0,0,z,0,
- 0,0,0,1);
-
- mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].mult_right(scale_mat);
- mMatHash[mMatrixMode]++;
- }
-}
-
-void LLRender::ortho(F32 left, F32 right, F32 bottom, F32 top, F32 zNear, F32 zFar)
-{
- flush();
-
- {
-
- glh::matrix4f ortho_mat(2.f/(right-left),0,0, -(right+left)/(right-left),
- 0,2.f/(top-bottom),0, -(top+bottom)/(top-bottom),
- 0,0,-2.f/(zFar-zNear), -(zFar+zNear)/(zFar-zNear),
- 0,0,0,1);
-
- mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].mult_right(ortho_mat);
- mMatHash[mMatrixMode]++;
- }
-}
-
-void LLRender::rotatef(const GLfloat& a, const GLfloat& x, const GLfloat& y, const GLfloat& z)
-{
- flush();
-
- {
- F32 r = a * DEG_TO_RAD;
-
- F32 c = cosf(r);
- F32 s = sinf(r);
-
- F32 ic = 1.f-c;
-
- glh::matrix4f rot_mat(x*x*ic+c, x*y*ic-z*s, x*z*ic+y*s, 0,
- x*y*ic+z*s, y*y*ic+c, y*z*ic-x*s, 0,
- x*z*ic-y*s, y*z*ic+x*s, z*z*ic+c, 0,
- 0,0,0,1);
-
- mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].mult_right(rot_mat);
- mMatHash[mMatrixMode]++;
- }
-}
-
-void LLRender::pushMatrix()
-{
- flush();
-
- {
- if (mMatIdx[mMatrixMode] < LL_MATRIX_STACK_DEPTH-1)
- {
- mMatrix[mMatrixMode][mMatIdx[mMatrixMode]+1] = mMatrix[mMatrixMode][mMatIdx[mMatrixMode]];
- ++mMatIdx[mMatrixMode];
- }
- else
- {
- LL_WARNS() << "Matrix stack overflow." << LL_ENDL;
- }
- }
-}
-
-void LLRender::popMatrix()
-{
- flush();
- {
- if (mMatIdx[mMatrixMode] > 0)
- {
- --mMatIdx[mMatrixMode];
- mMatHash[mMatrixMode]++;
- }
- else
- {
- LL_WARNS() << "Matrix stack underflow." << LL_ENDL;
- }
- }
-}
-
-void LLRender::loadMatrix(const GLfloat* m)
-{
- flush();
- {
- mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].set_value((GLfloat*) m);
- mMatHash[mMatrixMode]++;
- }
-}
-
-void LLRender::multMatrix(const GLfloat* m)
-{
- flush();
- {
- glh::matrix4f mat((GLfloat*) m);
-
- mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].mult_right(mat);
- mMatHash[mMatrixMode]++;
- }
-}
-
-void LLRender::matrixMode(eMatrixMode mode)
-{
- if (mode == MM_TEXTURE)
- {
- U32 tex_index = gGL.getCurrentTexUnitIndex();
- // the shaders don't actually reference anything beyond texture_matrix0/1 outside of terrain rendering
- llassert(tex_index <= 3);
- mode = eMatrixMode(MM_TEXTURE0 + tex_index);
- if (mode > MM_TEXTURE3)
- {
- // getCurrentTexUnitIndex() can go as high as 32 (LL_NUM_TEXTURE_LAYERS)
- // Large value will result in a crash at mMatrix
- LL_WARNS_ONCE() << "Attempted to assign matrix mode out of bounds: " << mode << LL_ENDL;
- mode = MM_TEXTURE0;
- }
- }
-
- mMatrixMode = mode;
-}
-
-LLRender::eMatrixMode LLRender::getMatrixMode()
-{
- if (mMatrixMode >= MM_TEXTURE0 && mMatrixMode <= MM_TEXTURE3)
- { //always return MM_TEXTURE if current matrix mode points at any texture matrix
- return MM_TEXTURE;
- }
-
- return mMatrixMode;
-}
-
-
-void LLRender::loadIdentity()
-{
- flush();
-
- {
- llassert_always(mMatrixMode < NUM_MATRIX_MODES) ;
-
- mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].make_identity();
- mMatHash[mMatrixMode]++;
- }
-}
-
-const glh::matrix4f& LLRender::getModelviewMatrix()
-{
- return mMatrix[MM_MODELVIEW][mMatIdx[MM_MODELVIEW]];
-}
-
-const glh::matrix4f& LLRender::getProjectionMatrix()
-{
- return mMatrix[MM_PROJECTION][mMatIdx[MM_PROJECTION]];
-}
-
-void LLRender::translateUI(F32 x, F32 y, F32 z)
-{
- if (mUIOffset.empty())
- {
- LL_ERRS() << "Need to push a UI translation frame before offsetting" << LL_ENDL;
- }
-
- 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())
- {
- LL_ERRS() << "Need to push a UI transformation frame before scaling." << LL_ENDL;
- }
-
- 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())
- {
- LL_ERRS() << "UI offset stack blown." << LL_ENDL;
- }
- mUIOffset.pop_back();
- mUIScale.pop_back();
-}
-
-LLVector3 LLRender::getUITranslation()
-{
- if (mUIOffset.empty())
- {
- return LLVector3(0,0,0);
- }
- return mUIOffset.back();
-}
-
-LLVector3 LLRender::getUIScale()
-{
- if (mUIScale.empty())
- {
- return LLVector3(1,1,1);
- }
- return mUIScale.back();
-}
-
-
-void LLRender::loadUIIdentity()
-{
- if (mUIOffset.empty())
- {
- LL_ERRS() << "Need to push UI translation frame before clearing offset." << LL_ENDL;
- }
- 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();
-
- if (mCurrColorMask[0] != writeColorR ||
- mCurrColorMask[1] != writeColorG ||
- mCurrColorMask[2] != writeColorB ||
- mCurrColorMask[3] != writeAlpha)
- {
- 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:
- LL_ERRS() << "Unknown Scene Blend Type: " << type << LL_ENDL;
- break;
- }
-}
-
-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 (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();
-
- glBlendFuncSeparate(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
- {
- LL_DEBUGS() << "Non-existing texture unit layer requested: " << index << LL_ENDL;
- return &mDummyTexUnit;
- }
-}
-
-LLLightState* LLRender::getLight(U32 index)
-{
- if (index < mLightState.size())
- {
- return &mLightState[index];
- }
-
- return NULL;
-}
-
-void LLRender::setAmbientLightColor(const LLColor4& color)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE
- if (color != mAmbientLightColor)
- {
- ++mLightHash;
- mAmbientLightColor = color;
- }
-}
-
-bool LLRender::verifyTexUnitActive(U32 unitToVerify)
-{
- if (mCurrTextureUnitIndex == unitToVerify)
- {
- return true;
- }
- else
- {
- LL_WARNS() << "TexUnit currently active: " << mCurrTextureUnitIndex << " (expecting " << unitToVerify << ")" << LL_ENDL;
- return false;
- }
-}
-
-void LLRender::clearErrors()
-{
- while (glGetError())
- {
- //loop until no more error flags left
- }
-}
-
-void LLRender::begin(const GLuint& mode)
-{
- if (mode != mMode)
- {
- if (mode == LLRender::QUADS)
- {
- mQuadCycle = 1;
- }
-
- if (mMode == LLRender::QUADS ||
- mMode == LLRender::LINES ||
- mMode == LLRender::TRIANGLES ||
- mMode == LLRender::POINTS)
- {
- flush();
- }
- else if (mCount != 0)
- {
- LL_ERRS() << "gGL.begin() called redundantly." << LL_ENDL;
- }
-
- mMode = mode;
- }
-}
-
-void LLRender::end()
-{
- if (mCount == 0)
- {
- return;
- //IMM_ERRS << "GL begin and end called with no vertices specified." << LL_ENDL;
- }
-
- if ((mMode != LLRender::QUADS &&
- mMode != LLRender::LINES &&
- mMode != LLRender::TRIANGLES &&
- mMode != LLRender::POINTS) ||
- mCount > 2048)
- {
- flush();
- }
-}
-void LLRender::flush()
-{
- if (mCount > 0)
- {
- LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE;
- llassert(LLGLSLShader::sCurBoundShaderPtr != nullptr);
- if (!mUIOffset.empty())
- {
- sUICalls++;
- sUIVerts += mCount;
- }
-
- //store mCount in a local variable to avoid re-entrance (drawArrays may call flush)
- U32 count = mCount;
-
- if (mMode == LLRender::QUADS && !sGLCoreProfile)
- {
- if (mCount%4 != 0)
- {
- count -= (mCount % 4);
- LL_WARNS() << "Incomplete quad requested." << LL_ENDL;
- }
- }
-
- if (mMode == LLRender::TRIANGLES)
- {
- if (mCount%3 != 0)
- {
- count -= (mCount % 3);
- LL_WARNS() << "Incomplete triangle requested." << LL_ENDL;
- }
- }
-
- if (mMode == LLRender::LINES)
- {
- if (mCount%2 != 0)
- {
- count -= (mCount % 2);
- LL_WARNS() << "Incomplete line requested." << LL_ENDL;
- }
- }
-
- mCount = 0;
-
- if (mBuffer)
- {
-
- HBXXH64 hash;
- U32 attribute_mask = LLGLSLShader::sCurBoundShaderPtr->mAttributeMask;
-
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb cache hash");
-
- hash.update((U8*)mVerticesp.get(), count * sizeof(LLVector4a));
- if (attribute_mask & LLVertexBuffer::MAP_TEXCOORD0)
- {
- hash.update((U8*)mTexcoordsp.get(), count * sizeof(LLVector2));
- }
-
- if (attribute_mask & LLVertexBuffer::MAP_COLOR)
- {
- hash.update((U8*)mColorsp.get(), count * sizeof(LLColor4U));
- }
-
- hash.finalize();
- }
-
-
- U64 vhash = hash.digest();
-
- // check the VB cache before making a new vertex buffer
- // This is a giant hack to deal with (mostly) our terrible UI rendering code
- // that was built on top of OpenGL immediate mode. Huge performance wins
- // can be had by not uploading geometry to VRAM unless absolutely necessary.
- // Most of our usage of the "immediate mode" style draw calls is actually
- // sending the same geometry over and over again.
- // To leverage this, we maintain a running hash of the vertex stream being
- // built up before a flush, and then check that hash against a VB
- // cache just before creating a vertex buffer in VRAM
- std::unordered_map<U64, LLVBCache>::iterator cache = sVBCache.find(vhash);
-
- LLPointer<LLVertexBuffer> vb;
-
- if (cache != sVBCache.end())
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb cache hit");
- // cache hit, just use the cached buffer
- vb = cache->second.vb;
- cache->second.touched = std::chrono::steady_clock::now();
- }
- else
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb cache miss");
- vb = new LLVertexBuffer(attribute_mask);
- vb->allocateBuffer(count, 0);
-
- vb->setBuffer();
-
- vb->setPositionData((LLVector4a*) mVerticesp.get());
-
- if (attribute_mask & LLVertexBuffer::MAP_TEXCOORD0)
- {
- vb->setTexCoordData(mTexcoordsp.get());
- }
-
- if (attribute_mask & LLVertexBuffer::MAP_COLOR)
- {
- vb->setColorData(mColorsp.get());
- }
-
- vb->unbind();
-
- sVBCache[vhash] = { vb , std::chrono::steady_clock::now() };
-
- static U32 miss_count = 0;
- miss_count++;
- if (miss_count > 1024)
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb cache clean");
- miss_count = 0;
- auto now = std::chrono::steady_clock::now();
-
- using namespace std::chrono_literals;
- // every 1024 misses, clean the cache of any VBs that haven't been touched in the last second
- for (std::unordered_map<U64, LLVBCache>::iterator iter = sVBCache.begin(); iter != sVBCache.end(); )
- {
- if (now - iter->second.touched > 1s)
- {
- iter = sVBCache.erase(iter);
- }
- else
- {
- ++iter;
- }
- }
- }
- }
-
- vb->setBuffer();
-
- if (mMode == LLRender::QUADS && sGLCoreProfile)
- {
- vb->drawArrays(LLRender::TRIANGLES, 0, count);
- mQuadCycle = 1;
- }
- else
- {
- vb->drawArrays(mMode, 0, count);
- }
- }
- else
- {
- // mBuffer is present in main thread and not present in an image thread
- LL_ERRS() << "A flush call from outside main rendering thread" << LL_ENDL;
- }
-
-
- mVerticesp[0] = mVerticesp[count];
- mTexcoordsp[0] = mTexcoordsp[count];
- mColorsp[0] = mColorsp[count];
-
- 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 > 2048)
- { //break when buffer gets reasonably full to keep GL command buffers happy and avoid overflow below
- switch (mMode)
- {
- case LLRender::POINTS: flush(); break;
- case LLRender::TRIANGLES: if (mCount%3==0) flush(); break;
- case LLRender::QUADS: if(mCount%4 == 0) flush(); break;
- case LLRender::LINES: if (mCount%2 == 0) flush(); break;
- }
- }
-
- if (mCount > 4094)
- {
- // LL_WARNS() << "GL immediate mode overflow. Some geometry not drawn." << LL_ENDL;
- 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;
- }
-
- if (mMode == LLRender::QUADS && LLRender::sGLCoreProfile)
- {
- mQuadCycle++;
- if (mQuadCycle == 4)
- { //copy two vertices so fourth quad element will add a triangle
- mQuadCycle = 0;
-
- mCount++;
- mVerticesp[mCount] = mVerticesp[mCount-3];
- mColorsp[mCount] = mColorsp[mCount-3];
- mTexcoordsp[mCount] = mTexcoordsp[mCount-3];
-
- mCount++;
- mVerticesp[mCount] = mVerticesp[mCount-2];
- mColorsp[mCount] = mColorsp[mCount-2];
- mTexcoordsp[mCount] = mTexcoordsp[mCount-2];
- }
- }
-
- 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)
- {
- // LL_WARNS() << "GL immediate mode overflow. Some geometry not drawn." << LL_ENDL;
- return;
- }
-
- if (sGLCoreProfile && mMode == LLRender::QUADS)
- { //quads are deprecated, convert to triangle list
- S32 i = 0;
-
- while (i < vert_count)
- {
- //read first three
- mVerticesp[mCount++] = verts[i++];
- mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
- mColorsp[mCount] = mColorsp[mCount-1];
-
- mVerticesp[mCount++] = verts[i++];
- mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
- mColorsp[mCount] = mColorsp[mCount-1];
-
- mVerticesp[mCount++] = verts[i++];
- mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
- mColorsp[mCount] = mColorsp[mCount-1];
-
- //copy two
- mVerticesp[mCount++] = verts[i-3];
- mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
- mColorsp[mCount] = mColorsp[mCount-1];
-
- mVerticesp[mCount++] = verts[i-1];
- mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
- mColorsp[mCount] = mColorsp[mCount-1];
-
- //copy last one
- mVerticesp[mCount++] = verts[i++];
- mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
- mColorsp[mCount] = mColorsp[mCount-1];
- }
- }
- else
- {
- for (S32 i = 0; i < vert_count; i++)
- {
- mVerticesp[mCount] = verts[i];
-
- mCount++;
- mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
- mColorsp[mCount] = mColorsp[mCount-1];
- }
- }
-
- if( mCount > 0 ) // ND: Guard against crashes if mCount is zero, yes it can happen
- mVerticesp[mCount] = mVerticesp[mCount-1];
-}
-
-void LLRender::vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, S32 vert_count)
-{
- if (mCount + vert_count > 4094)
- {
- // LL_WARNS() << "GL immediate mode overflow. Some geometry not drawn." << LL_ENDL;
- return;
- }
-
- if (sGLCoreProfile && mMode == LLRender::QUADS)
- { //quads are deprecated, convert to triangle list
- S32 i = 0;
-
- while (i < vert_count)
- {
- //read first three
- mVerticesp[mCount] = verts[i];
- mTexcoordsp[mCount++] = uvs[i++];
- mColorsp[mCount] = mColorsp[mCount-1];
-
- mVerticesp[mCount] = verts[i];
- mTexcoordsp[mCount++] = uvs[i++];
- mColorsp[mCount] = mColorsp[mCount-1];
-
- mVerticesp[mCount] = verts[i];
- mTexcoordsp[mCount++] = uvs[i++];
- mColorsp[mCount] = mColorsp[mCount-1];
-
- //copy last two
- mVerticesp[mCount] = verts[i-3];
- mTexcoordsp[mCount++] = uvs[i-3];
- mColorsp[mCount] = mColorsp[mCount-1];
-
- mVerticesp[mCount] = verts[i-1];
- mTexcoordsp[mCount++] = uvs[i-1];
- mColorsp[mCount] = mColorsp[mCount-1];
-
- //copy last one
- mVerticesp[mCount] = verts[i];
- mTexcoordsp[mCount++] = uvs[i++];
- mColorsp[mCount] = mColorsp[mCount-1];
- }
- }
- else
- {
- for (S32 i = 0; i < vert_count; i++)
- {
- mVerticesp[mCount] = verts[i];
- mTexcoordsp[mCount] = uvs[i];
-
- mCount++;
- mColorsp[mCount] = mColorsp[mCount-1];
- }
- }
-
- if (mCount > 0)
- {
- 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)
- {
- // LL_WARNS() << "GL immediate mode overflow. Some geometry not drawn." << LL_ENDL;
- return;
- }
-
-
- if (sGLCoreProfile && mMode == LLRender::QUADS)
- { //quads are deprecated, convert to triangle list
- S32 i = 0;
-
- while (i < vert_count)
- {
- //read first three
- mVerticesp[mCount] = verts[i];
- mTexcoordsp[mCount] = uvs[i];
- mColorsp[mCount++] = colors[i++];
-
- mVerticesp[mCount] = verts[i];
- mTexcoordsp[mCount] = uvs[i];
- mColorsp[mCount++] = colors[i++];
-
- mVerticesp[mCount] = verts[i];
- mTexcoordsp[mCount] = uvs[i];
- mColorsp[mCount++] = colors[i++];
-
- //copy last two
- mVerticesp[mCount] = verts[i-3];
- mTexcoordsp[mCount] = uvs[i-3];
- mColorsp[mCount++] = colors[i-3];
-
- mVerticesp[mCount] = verts[i-1];
- mTexcoordsp[mCount] = uvs[i-1];
- mColorsp[mCount++] = colors[i-1];
-
- //copy last one
- mVerticesp[mCount] = verts[i];
- mTexcoordsp[mCount] = uvs[i];
- mColorsp[mCount++] = colors[i++];
- }
- }
- else
- {
- for (S32 i = 0; i < vert_count; i++)
- {
- mVerticesp[mCount] = verts[i];
- mTexcoordsp[mCount] = uvs[i];
- mColorsp[mCount] = colors[i];
-
- mCount++;
- }
- }
-
- if (mCount > 0)
- {
- 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)
-{
- if (!LLGLSLShader::sCurBoundShaderPtr || LLGLSLShader::sCurBoundShaderPtr->mAttributeMask & LLVertexBuffer::MAP_COLOR)
- {
- mColorsp[mCount] = LLColor4U(r,g,b,a);
- }
- else
- { //not using shaders or shader reads color from a uniform
- diffuseColor4ub(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::diffuseColor3f(F32 r, F32 g, F32 b)
-{
- LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
- llassert(shader != NULL);
-
- if (shader)
- {
- shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, r,g,b,1.f);
- }
- else
- {
- glColor3f(r,g,b);
- }
-}
-
-void LLRender::diffuseColor3fv(const F32* c)
-{
- LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
- llassert(shader != NULL);
-
- if (shader)
- {
- shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, c[0], c[1], c[2], 1.f);
- }
- else
- {
- glColor3fv(c);
- }
-}
-
-void LLRender::diffuseColor4f(F32 r, F32 g, F32 b, F32 a)
-{
- LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
- llassert(shader != NULL);
-
- if (shader)
- {
- shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, r,g,b,a);
- }
- else
- {
- glColor4f(r,g,b,a);
- }
-}
-
-void LLRender::diffuseColor4fv(const F32* c)
-{
- LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
- llassert(shader != NULL);
-
- if (shader)
- {
- shader->uniform4fv(LLShaderMgr::DIFFUSE_COLOR, 1, c);
- }
- else
- {
- glColor4fv(c);
- }
-}
-
-void LLRender::diffuseColor4ubv(const U8* c)
-{
- LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
- llassert(shader != NULL);
-
- if (shader)
- {
- shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, c[0]/255.f, c[1]/255.f, c[2]/255.f, c[3]/255.f);
- }
- else
- {
- glColor4ubv(c);
- }
-}
-
-void LLRender::diffuseColor4ub(U8 r, U8 g, U8 b, U8 a)
-{
- LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
- llassert(shader != NULL);
-
- if (shader)
- {
- shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, r/255.f, g/255.f, b/255.f, a/255.f);
- }
- else
- {
- glColor4ub(r,g,b,a);
- }
-}
-
-
-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;
-}
-
-
-
-glh::matrix4f copy_matrix(F32* src)
-{
- glh::matrix4f ret;
- ret.set_value(src);
- return ret;
-}
-
-glh::matrix4f get_current_modelview()
-{
- return copy_matrix(gGLModelView);
-}
-
-glh::matrix4f get_current_projection()
-{
- return copy_matrix(gGLProjection);
-}
-
-glh::matrix4f get_last_modelview()
-{
- return copy_matrix(gGLLastModelView);
-}
-
-glh::matrix4f get_last_projection()
-{
- return copy_matrix(gGLLastProjection);
-}
-
-void copy_matrix(const glh::matrix4f& src, F32* dst)
-{
- for (U32 i = 0; i < 16; i++)
- {
- dst[i] = src.m[i];
- }
-}
-
-void set_current_modelview(const glh::matrix4f& mat)
-{
- copy_matrix(mat, gGLModelView);
-}
-
-void set_current_projection(glh::matrix4f& mat)
-{
- copy_matrix(mat, gGLProjection);
-}
-
-glh::matrix4f gl_ortho(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat znear, GLfloat zfar)
-{
- glh::matrix4f ret(
- 2.f/(right-left), 0.f, 0.f, -(right+left)/(right-left),
- 0.f, 2.f/(top-bottom), 0.f, -(top+bottom)/(top-bottom),
- 0.f, 0.f, -2.f/(zfar-znear), -(zfar+znear)/(zfar-znear),
- 0.f, 0.f, 0.f, 1.f);
-
- return ret;
-}
-
-glh::matrix4f gl_perspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar)
-{
- GLfloat f = 1.f/tanf(DEG_TO_RAD*fovy/2.f);
-
- return glh::matrix4f(f/aspect, 0, 0, 0,
- 0, f, 0, 0,
- 0, 0, (zFar+zNear)/(zNear-zFar), (2.f*zFar*zNear)/(zNear-zFar),
- 0, 0, -1.f, 0);
-}
-
-glh::matrix4f gl_lookat(LLVector3 eye, LLVector3 center, LLVector3 up)
-{
- LLVector3 f = center-eye;
- f.normVec();
- up.normVec();
- LLVector3 s = f % up;
- LLVector3 u = s % f;
-
- return glh::matrix4f(s[0], s[1], s[2], 0,
- u[0], u[1], u[2], 0,
- -f[0], -f[1], -f[2], 0,
- 0, 0, 0, 1);
-
-}
+ /**
+ * @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 "llglslshader.h"
+#include "llimagegl.h"
+#include "llrendertarget.h"
+#include "lltexture.h"
+#include "llshadermgr.h"
+#include "hbxxh.h"
+
+#if LL_WINDOWS
+extern void APIENTRY gl_debug_callback(GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar* message,
+ GLvoid* userParam)
+;
+#endif
+
+thread_local LLRender gGL;
+
+// Handy copies of last good GL matrices
+F32 gGLModelView[16];
+F32 gGLLastModelView[16];
+F32 gGLLastProjection[16];
+F32 gGLProjection[16];
+
+// transform from last frame's camera space to this frame's camera space (and inverse)
+F32 gGLDeltaModelView[16];
+F32 gGLInverseDeltaModelView[16];
+
+S32 gGLViewport[4];
+
+
+U32 LLRender::sUICalls = 0;
+U32 LLRender::sUIVerts = 0;
+U32 LLTexUnit::sWhiteTexture = 0;
+bool LLRender::sGLCoreProfile = false;
+bool LLRender::sNsightDebugSupport = false;
+LLVector2 LLRender::sUIGLScaleFactor = LLVector2(1.f, 1.f);
+
+struct LLVBCache
+{
+ LLPointer<LLVertexBuffer> vb;
+ std::chrono::steady_clock::time_point touched;
+};
+
+static std::unordered_map<U64, LLVBCache> sVBCache;
+
+static const GLenum sGLTextureType[] =
+{
+ GL_TEXTURE_2D,
+ GL_TEXTURE_RECTANGLE,
+ GL_TEXTURE_CUBE_MAP,
+ GL_TEXTURE_CUBE_MAP_ARRAY,
+ GL_TEXTURE_2D_MULTISAMPLE,
+ GL_TEXTURE_3D
+};
+
+static const GLint sGLAddressMode[] =
+{
+ GL_REPEAT,
+ GL_MIRRORED_REPEAT,
+ GL_CLAMP_TO_EDGE
+};
+
+const U32 immediate_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_TEXCOORD0;
+
+static const 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),
+ mCurrColorScale(1), mCurrAlphaScale(1), mCurrTexture(0), mTexColorSpace(TCS_LINEAR),
+ mHasMipMaps(false),
+ mIndex(index)
+{
+ llassert_always(index < (S32)LL_NUM_TEXTURE_LAYERS);
+}
+
+//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();
+
+ glActiveTexture(GL_TEXTURE0 + mIndex);
+
+ if (mCurrTexType != TT_NONE)
+ {
+ glBindTexture(sGLTextureType[mCurrTexType], mCurrTexture);
+ }
+ else
+ {
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ setTextureColorSpace(mTexColorSpace);
+}
+
+void LLTexUnit::activate(void)
+{
+ if (mIndex < 0) return;
+
+ if ((S32)gGL.mCurrTextureUnitIndex != mIndex || gGL.mDirty)
+ {
+ gGL.flush();
+ glActiveTexture(GL_TEXTURE0 + 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();
+ }
+}
+
+void LLTexUnit::disable(void)
+{
+ if (mIndex < 0) return;
+
+ if (mCurrTexType != TT_NONE)
+ {
+ unbind(mCurrTexType);
+ mCurrTexType = TT_NONE;
+ }
+}
+
+void LLTexUnit::bindFast(LLTexture* texture)
+{
+ LLImageGL* gl_tex = texture->getGLTexture();
+ texture->setActive();
+ glActiveTexture(GL_TEXTURE0 + mIndex);
+ gGL.mCurrTextureUnitIndex = mIndex;
+ mCurrTexture = gl_tex->getTexName();
+ if (!mCurrTexture)
+ {
+ LL_PROFILE_ZONE_NAMED("MISSING TEXTURE");
+ //if deleted, will re-generate it immediately
+ texture->forceImmediateUpdate();
+ gl_tex->forceUpdateBindStats();
+ texture->bindDefaultImage(mIndex);
+ }
+ glBindTexture(sGLTextureType[gl_tex->getTarget()], mCurrTexture);
+ mHasMipMaps = gl_tex->mHasMipMaps;
+}
+
+bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE;
+ stop_glerror();
+ if (mIndex >= 0)
+ {
+ gGL.flush();
+
+ LLImageGL* gl_tex = NULL ;
+
+ if (texture != NULL && (gl_tex = texture->getGLTexture()))
+ {
+ if (gl_tex->getTexName()) //if texture exists
+ {
+ //in audit, replace the selected texture by the default one.
+ 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())
+ {
+ texture->setActive() ;
+ texture->updateBindStatsForTester() ;
+ }
+ mHasMipMaps = gl_tex->mHasMipMaps;
+ if (gl_tex->mTexOptionsDirty)
+ {
+ gl_tex->mTexOptionsDirty = false;
+ setTextureAddressMode(gl_tex->mAddressMode);
+ setTextureFilteringOption(gl_tex->mFilterOption);
+ }
+ setTextureColorSpace(mTexColorSpace);
+ }
+ }
+ else
+ {
+ //if deleted, will re-generate it immediately
+ texture->forceImmediateUpdate() ;
+
+ gl_tex->forceUpdateBindStats() ;
+ return texture->bindDefaultImage(mIndex);
+ }
+ }
+ else
+ {
+ if (texture)
+ {
+ LL_DEBUGS() << "NULL LLTexUnit::bind GL image" << LL_ENDL;
+ }
+ else
+ {
+ LL_DEBUGS() << "NULL LLTexUnit::bind texture" << LL_ENDL;
+ }
+ return false;
+ }
+ }
+ else
+ { // mIndex < 0
+ return false;
+ }
+
+ return true;
+}
+
+bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind, S32 usename)
+{
+ stop_glerror();
+ if (mIndex < 0) return false;
+
+ U32 texname = usename ? usename : texture->getTexName();
+
+ if(!texture)
+ {
+ LL_DEBUGS() << "NULL LLTexUnit::bind texture" << LL_ENDL;
+ return false;
+ }
+
+ if(!texname)
+ {
+ if(LLImageGL::sDefaultGLTexture && LLImageGL::sDefaultGLTexture->getTexName())
+ {
+ return bind(LLImageGL::sDefaultGLTexture) ;
+ }
+ stop_glerror();
+ return false ;
+ }
+
+ if ((mCurrTexture != texname) || forceBind)
+ {
+ gGL.flush();
+ stop_glerror();
+ activate();
+ stop_glerror();
+ enable(texture->getTarget());
+ stop_glerror();
+ mCurrTexture = texname;
+ glBindTexture(sGLTextureType[texture->getTarget()], mCurrTexture);
+ stop_glerror();
+ texture->updateBindStats();
+ mHasMipMaps = texture->mHasMipMaps;
+ if (texture->mTexOptionsDirty)
+ {
+ stop_glerror();
+ texture->mTexOptionsDirty = false;
+ setTextureAddressMode(texture->mAddressMode);
+ setTextureFilteringOption(texture->mFilterOption);
+ stop_glerror();
+ }
+ setTextureColorSpace(mTexColorSpace);
+ }
+
+ stop_glerror();
+
+ return true;
+}
+
+bool LLTexUnit::bind(LLCubeMap* cubeMap)
+{
+ if (mIndex < 0) return false;
+
+ gGL.flush();
+
+ if (cubeMap == NULL)
+ {
+ LL_WARNS() << "NULL LLTexUnit::bind cubemap" << LL_ENDL;
+ return false;
+ }
+
+ if (mCurrTexture != cubeMap->mImages[0]->getTexName())
+ {
+ if (LLCubeMap::sUseCubeMaps)
+ {
+ activate();
+ enable(LLTexUnit::TT_CUBE_MAP);
+ mCurrTexture = cubeMap->mImages[0]->getTexName();
+ glBindTexture(GL_TEXTURE_CUBE_MAP, mCurrTexture);
+ mHasMipMaps = cubeMap->mImages[0]->mHasMipMaps;
+ cubeMap->mImages[0]->updateBindStats();
+ if (cubeMap->mImages[0]->mTexOptionsDirty)
+ {
+ cubeMap->mImages[0]->mTexOptionsDirty = false;
+ setTextureAddressMode(cubeMap->mImages[0]->mAddressMode);
+ setTextureFilteringOption(cubeMap->mImages[0]->mFilterOption);
+ }
+ setTextureColorSpace(mTexColorSpace);
+ return true;
+ }
+ else
+ {
+ LL_WARNS() << "Using cube map without extension!" << LL_ENDL;
+ return false;
+ }
+ }
+ return true;
+}
+
+// LLRenderTarget is unavailible on the mapserver since it uses FBOs.
+bool LLTexUnit::bind(LLRenderTarget* renderTarget, bool bindDepth)
+{
+ if (mIndex < 0) return false;
+
+ gGL.flush();
+
+ if (bindDepth)
+ {
+ llassert(renderTarget->getDepth()); // target MUST have a depth buffer attachment
+
+ bindManual(renderTarget->getUsage(), renderTarget->getDepth());
+ }
+ else
+ {
+ bindManual(renderTarget->getUsage(), renderTarget->getTexture());
+ }
+
+ return true;
+}
+
+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;
+ setTextureColorSpace(mTexColorSpace);
+ }
+ return true;
+}
+
+void LLTexUnit::unbind(eTextureType type)
+{
+ stop_glerror();
+
+ if (mIndex < 0) return;
+
+ //always flush and activate for consistency
+ // some code paths assume unbind always flushes and sets the active texture
+ gGL.flush();
+ activate();
+
+ // Disabled caching of binding state.
+ if (mCurrTexType == type)
+ {
+ mCurrTexture = 0;
+
+ // Always make sure our texture color space is reset to linear. SRGB sampling should be opt-in in the vast majority of cases. Also prevents color space "popping".
+ mTexColorSpace = TCS_LINEAR;
+ if (type == LLTexUnit::TT_TEXTURE)
+ {
+ glBindTexture(sGLTextureType[type], sWhiteTexture);
+ }
+ else
+ {
+ glBindTexture(sGLTextureType[type], 0);
+ }
+ stop_glerror();
+ }
+}
+
+void LLTexUnit::unbindFast(eTextureType type)
+{
+ activate();
+
+ // Disabled caching of binding state.
+ if (mCurrTexType == type)
+ {
+ mCurrTexture = 0;
+
+ // Always make sure our texture color space is reset to linear. SRGB sampling should be opt-in in the vast majority of cases. Also prevents color space "popping".
+ mTexColorSpace = TCS_LINEAR;
+ if (type == LLTexUnit::TT_TEXTURE)
+ {
+ glBindTexture(sGLTextureType[type], sWhiteTexture);
+ }
+ else
+ {
+ 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, GL_TEXTURE_WRAP_R, sGLAddressMode[mode]);
+ }
+}
+
+void LLTexUnit::setTextureFilteringOption(LLTexUnit::eTextureFilterOptions option)
+{
+ if (mIndex < 0 || mCurrTexture == 0 || mCurrTexType == LLTexUnit::TT_MULTISAMPLE_TEXTURE) 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)
+ {
+ if (mHasMipMaps)
+ {
+ glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
+ }
+ else
+ {
+ glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ }
+ }
+ else
+ {
+ if (mHasMipMaps)
+ {
+ glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
+ }
+ else
+ {
+ glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+ }
+
+ if (gGLManager.mGLVersion >= 4.59f)
+ {
+ if (LLImageGL::sGlobalUseAnisotropic && option == TFO_ANISOTROPIC)
+ {
+ glTexParameterf(sGLTextureType[mCurrTexType], GL_TEXTURE_MAX_ANISOTROPY, gGLManager.mMaxAnisotropy);
+ }
+ else
+ {
+ glTexParameterf(sGLTextureType[mCurrTexType], GL_TEXTURE_MAX_ANISOTROPY, 1.f);
+ }
+ }
+}
+
+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;
+
+ // 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;
+
+ // 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;
+
+ default:
+ LL_WARNS() << "Unknown eTextureBlendSrc: " << src << ". Using Vertex Color instead." << LL_ENDL;
+ return GL_PRIMARY_COLOR;
+ }
+}
+
+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:
+ LL_WARNS() << "Unknown eTextureBlendSrc: " << src << ". Using Source Color or Alpha instead." << LL_ENDL;
+ return (isAlpha) ? GL_SRC_ALPHA: GL_SRC_COLOR;
+ }
+}
+
+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, &activeTexture);
+ if ((GL_TEXTURE0 + mIndex) != activeTexture)
+ {
+ U32 set_unit = (activeTexture - GL_TEXTURE0);
+ LL_WARNS() << "Incorrect Texture Unit! Expected: " << set_unit << " Actual: " << mIndex << LL_ENDL;
+ }
+}
+
+void LLTexUnit::setTextureColorSpace(eTextureColorSpace space)
+{
+ mTexColorSpace = space;
+}
+
+LLLightState::LLLightState(S32 index)
+: mIndex(index),
+ mEnabled(false),
+ mConstantAtten(1.f),
+ mLinearAtten(0.f),
+ mQuadraticAtten(0.f),
+ mSpotExponent(0.f),
+ mSpotCutoff(180.f)
+{
+ if (mIndex == 0)
+ {
+ mDiffuse.set(1,1,1,1);
+ mDiffuseB.set(0,0,0,0);
+ mSpecular.set(1,1,1,1);
+ }
+
+ mSunIsPrimary = true;
+
+ mAmbient.set(0,0,0,1);
+ mPosition.set(0,0,1,0);
+ mSpotDirection.set(0,0,-1);
+}
+
+void LLLightState::enable()
+{
+ mEnabled = true;
+}
+
+void LLLightState::disable()
+{
+ mEnabled = false;
+}
+
+void LLLightState::setDiffuse(const LLColor4& diffuse)
+{
+ if (mDiffuse != diffuse)
+ {
+ ++gGL.mLightHash;
+ mDiffuse = diffuse;
+ }
+}
+
+void LLLightState::setDiffuseB(const LLColor4& diffuse)
+{
+ if (mDiffuseB != diffuse)
+ {
+ ++gGL.mLightHash;
+ mDiffuseB = diffuse;
+ }
+}
+
+void LLLightState::setSunPrimary(bool v)
+{
+ if (mSunIsPrimary != v)
+ {
+ ++gGL.mLightHash;
+ mSunIsPrimary = v;
+ }
+}
+
+void LLLightState::setSize(F32 v)
+{
+ if (mSize != v)
+ {
+ ++gGL.mLightHash;
+ mSize = v;
+ }
+}
+
+void LLLightState::setFalloff(F32 v)
+{
+ if (mFalloff != v)
+ {
+ ++gGL.mLightHash;
+ mFalloff = v;
+ }
+}
+
+void LLLightState::setAmbient(const LLColor4& ambient)
+{
+ if (mAmbient != ambient)
+ {
+ ++gGL.mLightHash;
+ mAmbient = ambient;
+ }
+}
+
+void LLLightState::setSpecular(const LLColor4& specular)
+{
+ if (mSpecular != specular)
+ {
+ ++gGL.mLightHash;
+ mSpecular = specular;
+ }
+}
+
+void LLLightState::setPosition(const LLVector4& position)
+{
+ //always set position because modelview matrix may have changed
+ ++gGL.mLightHash;
+ mPosition = position;
+ //transform position by current modelview matrix
+ glh::vec4f pos(position.mV);
+ const glh::matrix4f& mat = gGL.getModelviewMatrix();
+ mat.mult_matrix_vec(pos);
+ mPosition.set(pos.v);
+}
+
+void LLLightState::setConstantAttenuation(const F32& atten)
+{
+ if (mConstantAtten != atten)
+ {
+ mConstantAtten = atten;
+ ++gGL.mLightHash;
+ }
+}
+
+void LLLightState::setLinearAttenuation(const F32& atten)
+{
+ if (mLinearAtten != atten)
+ {
+ ++gGL.mLightHash;
+ mLinearAtten = atten;
+ }
+}
+
+void LLLightState::setQuadraticAttenuation(const F32& atten)
+{
+ if (mQuadraticAtten != atten)
+ {
+ ++gGL.mLightHash;
+ mQuadraticAtten = atten;
+ }
+}
+
+void LLLightState::setSpotExponent(const F32& exponent)
+{
+ if (mSpotExponent != exponent)
+ {
+ ++gGL.mLightHash;
+ mSpotExponent = exponent;
+ }
+}
+
+void LLLightState::setSpotCutoff(const F32& cutoff)
+{
+ if (mSpotCutoff != cutoff)
+ {
+ ++gGL.mLightHash;
+ mSpotCutoff = cutoff;
+ }
+}
+
+void LLLightState::setSpotDirection(const LLVector3& direction)
+{
+ //always set direction because modelview matrix may have changed
+ ++gGL.mLightHash;
+ mSpotDirection = direction;
+ //transform direction by current modelview matrix
+ glh::vec3f dir(direction.mV);
+ const glh::matrix4f& mat = gGL.getModelviewMatrix();
+ mat.mult_matrix_dir(dir);
+
+ mSpotDirection.set(dir.v);
+}
+
+LLRender::LLRender()
+ : mDirty(false),
+ mCount(0),
+ mQuadCycle(0),
+ mMode(LLRender::TRIANGLES),
+ mCurrTextureUnitIndex(0)
+{
+ for (U32 i = 0; i < LL_NUM_TEXTURE_LAYERS; i++)
+ {
+ mTexUnits[i].mIndex = i;
+ }
+
+ for (U32 i = 0; i < LL_NUM_LIGHT_UNITS; ++i)
+ {
+ mLightState[i].mIndex = i;
+ }
+
+ for (U32 i = 0; i < 4; i++)
+ {
+ mCurrColorMask[i] = true;
+ }
+
+ mCurrBlendColorSFactor = BF_UNDEF;
+ mCurrBlendAlphaSFactor = BF_UNDEF;
+ mCurrBlendColorDFactor = BF_UNDEF;
+ mCurrBlendAlphaDFactor = BF_UNDEF;
+
+ mMatrixMode = LLRender::MM_MODELVIEW;
+
+ for (U32 i = 0; i < NUM_MATRIX_MODES; ++i)
+ {
+ mMatIdx[i] = 0;
+ mMatHash[i] = 0;
+ mCurMatHash[i] = 0xFFFFFFFF;
+ }
+
+ mLightHash = 0;
+}
+
+LLRender::~LLRender()
+{
+ shutdown();
+}
+
+bool LLRender::init(bool needs_vertex_buffer)
+{
+#if LL_WINDOWS
+ if (gGLManager.mHasDebugOutput && gDebugGL)
+ { //setup debug output callback
+ //glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW_ARB, 0, NULL, GL_TRUE);
+ glDebugMessageCallback((GLDEBUGPROC) gl_debug_callback, NULL);
+ glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+ }
+#endif
+
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ gGL.setAmbientLightColor(LLColor4::black);
+
+ glCullFace(GL_BACK);
+
+ // necessary for reflection maps
+ glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+
+#if LL_WINDOWS
+ if (glGenVertexArrays == nullptr)
+ {
+ return false;
+ }
+#endif
+
+ { //bind a dummy vertex array object so we're core profile compliant
+ U32 ret;
+ glGenVertexArrays(1, &ret);
+ glBindVertexArray(ret);
+ }
+
+ if (needs_vertex_buffer)
+ {
+ initVertexBuffer();
+ }
+ return true;
+}
+
+void LLRender::initVertexBuffer()
+{
+ llassert_always(mBuffer.isNull());
+ stop_glerror();
+ mBuffer = new LLVertexBuffer(immediate_mask);
+ mBuffer->allocateBuffer(4096, 0);
+ mBuffer->getVertexStrider(mVerticesp);
+ mBuffer->getTexCoord0Strider(mTexcoordsp);
+ mBuffer->getColorStrider(mColorsp);
+ stop_glerror();
+}
+
+void LLRender::resetVertexBuffer()
+{
+ mBuffer = NULL;
+}
+
+void LLRender::shutdown()
+{
+ resetVertexBuffer();
+}
+
+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]);
+
+ flush();
+
+ mDirty = false;
+}
+
+void LLRender::syncLightState()
+{
+ LLGLSLShader *shader = LLGLSLShader::sCurBoundShaderPtr;
+
+ if (!shader)
+ {
+ return;
+ }
+
+ if (shader->mLightHash != mLightHash)
+ {
+ shader->mLightHash = mLightHash;
+
+ LLVector4 position[LL_NUM_LIGHT_UNITS];
+ LLVector3 direction[LL_NUM_LIGHT_UNITS];
+ LLVector4 attenuation[LL_NUM_LIGHT_UNITS];
+ LLVector3 diffuse[LL_NUM_LIGHT_UNITS];
+ LLVector3 diffuse_b[LL_NUM_LIGHT_UNITS];
+ bool sun_primary[LL_NUM_LIGHT_UNITS];
+ LLVector2 size[LL_NUM_LIGHT_UNITS];
+
+ for (U32 i = 0; i < LL_NUM_LIGHT_UNITS; i++)
+ {
+ LLLightState *light = &mLightState[i];
+
+ position[i] = light->mPosition;
+ direction[i] = light->mSpotDirection;
+ attenuation[i].set(light->mLinearAtten, light->mQuadraticAtten, light->mSpecular.mV[2], light->mSpecular.mV[3]);
+ diffuse[i].set(light->mDiffuse.mV);
+ diffuse_b[i].set(light->mDiffuseB.mV);
+ sun_primary[i] = light->mSunIsPrimary;
+ size[i].set(light->mSize, light->mFalloff);
+ }
+
+ shader->uniform4fv(LLShaderMgr::LIGHT_POSITION, LL_NUM_LIGHT_UNITS, position[0].mV);
+ shader->uniform3fv(LLShaderMgr::LIGHT_DIRECTION, LL_NUM_LIGHT_UNITS, direction[0].mV);
+ shader->uniform4fv(LLShaderMgr::LIGHT_ATTENUATION, LL_NUM_LIGHT_UNITS, attenuation[0].mV);
+ shader->uniform2fv(LLShaderMgr::LIGHT_DEFERRED_ATTENUATION, LL_NUM_LIGHT_UNITS, size[0].mV);
+ shader->uniform3fv(LLShaderMgr::LIGHT_DIFFUSE, LL_NUM_LIGHT_UNITS, diffuse[0].mV);
+ shader->uniform3fv(LLShaderMgr::LIGHT_AMBIENT, 1, mAmbientLightColor.mV);
+ shader->uniform1i(LLShaderMgr::SUN_UP_FACTOR, sun_primary[0] ? 1 : 0);
+ //shader->uniform3fv(LLShaderMgr::AMBIENT, 1, mAmbientLightColor.mV);
+ //shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, 1, diffuse[0].mV);
+ //shader->uniform3fv(LLShaderMgr::MOONLIGHT_COLOR, 1, diffuse_b[0].mV);
+ }
+}
+
+void LLRender::syncMatrices()
+{
+ static const U32 name[] =
+ {
+ LLShaderMgr::MODELVIEW_MATRIX,
+ LLShaderMgr::PROJECTION_MATRIX,
+ LLShaderMgr::TEXTURE_MATRIX0,
+ LLShaderMgr::TEXTURE_MATRIX1,
+ LLShaderMgr::TEXTURE_MATRIX2,
+ LLShaderMgr::TEXTURE_MATRIX3,
+ };
+
+ LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+
+ static glh::matrix4f cached_mvp;
+ static glh::matrix4f cached_inv_mdv;
+ static U32 cached_mvp_mdv_hash = 0xFFFFFFFF;
+ static U32 cached_mvp_proj_hash = 0xFFFFFFFF;
+
+ static glh::matrix4f cached_normal;
+ static U32 cached_normal_hash = 0xFFFFFFFF;
+
+ if (shader)
+ {
+ //llassert(shader);
+
+ bool mvp_done = false;
+
+ U32 i = MM_MODELVIEW;
+ if (mMatHash[MM_MODELVIEW] != shader->mMatHash[MM_MODELVIEW])
+ { //update modelview, normal, and MVP
+ glh::matrix4f& mat = mMatrix[MM_MODELVIEW][mMatIdx[MM_MODELVIEW]];
+
+ // if MDV has changed, update the cached inverse as well
+ if (cached_mvp_mdv_hash != mMatHash[MM_MODELVIEW])
+ {
+ cached_inv_mdv = mat.inverse();
+ }
+
+ shader->uniformMatrix4fv(name[MM_MODELVIEW], 1, GL_FALSE, mat.m);
+ shader->mMatHash[MM_MODELVIEW] = mMatHash[MM_MODELVIEW];
+
+ //update normal matrix
+ S32 loc = shader->getUniformLocation(LLShaderMgr::NORMAL_MATRIX);
+ if (loc > -1)
+ {
+ if (cached_normal_hash != mMatHash[i])
+ {
+ cached_normal = cached_inv_mdv.transpose();
+ cached_normal_hash = mMatHash[i];
+ }
+
+ glh::matrix4f& norm = cached_normal;
+
+ F32 norm_mat[] =
+ {
+ norm.m[0], norm.m[1], norm.m[2],
+ norm.m[4], norm.m[5], norm.m[6],
+ norm.m[8], norm.m[9], norm.m[10]
+ };
+
+ shader->uniformMatrix3fv(LLShaderMgr::NORMAL_MATRIX, 1, GL_FALSE, norm_mat);
+ }
+
+ if (shader->getUniformLocation(LLShaderMgr::INVERSE_MODELVIEW_MATRIX))
+ {
+ shader->uniformMatrix4fv(LLShaderMgr::INVERSE_MODELVIEW_MATRIX, 1, GL_FALSE, cached_inv_mdv.m);
+ }
+
+ //update MVP matrix
+ mvp_done = true;
+ loc = shader->getUniformLocation(LLShaderMgr::MODELVIEW_PROJECTION_MATRIX);
+ if (loc > -1)
+ {
+ U32 proj = MM_PROJECTION;
+
+ if (cached_mvp_mdv_hash != mMatHash[i] || cached_mvp_proj_hash != mMatHash[MM_PROJECTION])
+ {
+ cached_mvp = mat;
+ cached_mvp.mult_left(mMatrix[proj][mMatIdx[proj]]);
+ cached_mvp_mdv_hash = mMatHash[i];
+ cached_mvp_proj_hash = mMatHash[MM_PROJECTION];
+ }
+
+ shader->uniformMatrix4fv(LLShaderMgr::MODELVIEW_PROJECTION_MATRIX, 1, GL_FALSE, cached_mvp.m);
+ }
+ }
+
+ i = MM_PROJECTION;
+ if (mMatHash[MM_PROJECTION] != shader->mMatHash[MM_PROJECTION])
+ { //update projection matrix, normal, and MVP
+ glh::matrix4f& mat = mMatrix[MM_PROJECTION][mMatIdx[MM_PROJECTION]];
+
+ // GZ: This was previously disabled seemingly due to a bug involving the deferred renderer's regular pushing and popping of mats.
+ // We're reenabling this and cleaning up the code around that - that would've been the appropriate course initially.
+ // Anything beyond the standard proj and inv proj mats are special cases. Please setup special uniforms accordingly in the future.
+ if (shader->getUniformLocation(LLShaderMgr::INVERSE_PROJECTION_MATRIX))
+ {
+ glh::matrix4f inv_proj = mat.inverse();
+ shader->uniformMatrix4fv(LLShaderMgr::INVERSE_PROJECTION_MATRIX, 1, false, inv_proj.m);
+ }
+
+ // Used by some full screen effects - such as full screen lights, glow, etc.
+ if (shader->getUniformLocation(LLShaderMgr::IDENTITY_MATRIX))
+ {
+ shader->uniformMatrix4fv(LLShaderMgr::IDENTITY_MATRIX, 1, GL_FALSE, glh::matrix4f::identity().m);
+ }
+
+ shader->uniformMatrix4fv(name[MM_PROJECTION], 1, GL_FALSE, mat.m);
+ shader->mMatHash[MM_PROJECTION] = mMatHash[MM_PROJECTION];
+
+ if (!mvp_done)
+ {
+ //update MVP matrix
+ S32 loc = shader->getUniformLocation(LLShaderMgr::MODELVIEW_PROJECTION_MATRIX);
+ if (loc > -1)
+ {
+ if (cached_mvp_mdv_hash != mMatHash[MM_PROJECTION] || cached_mvp_proj_hash != mMatHash[MM_PROJECTION])
+ {
+ U32 mdv = MM_MODELVIEW;
+ cached_mvp = mat;
+ cached_mvp.mult_right(mMatrix[mdv][mMatIdx[mdv]]);
+ cached_mvp_mdv_hash = mMatHash[MM_MODELVIEW];
+ cached_mvp_proj_hash = mMatHash[MM_PROJECTION];
+ }
+
+ shader->uniformMatrix4fv(LLShaderMgr::MODELVIEW_PROJECTION_MATRIX, 1, GL_FALSE, cached_mvp.m);
+ }
+ }
+ }
+
+ for (i = MM_TEXTURE0; i < NUM_MATRIX_MODES; ++i)
+ {
+ if (mMatHash[i] != shader->mMatHash[i])
+ {
+ shader->uniformMatrix4fv(name[i], 1, GL_FALSE, mMatrix[i][mMatIdx[i]].m);
+ shader->mMatHash[i] = mMatHash[i];
+ }
+ }
+
+
+ if (shader->mFeatures.hasLighting || shader->mFeatures.calculatesLighting || shader->mFeatures.calculatesAtmospherics)
+ { //also sync light state
+ syncLightState();
+ }
+ }
+}
+
+void LLRender::translatef(const GLfloat& x, const GLfloat& y, const GLfloat& z)
+{
+ flush();
+
+ {
+ glh::matrix4f trans_mat(1,0,0,x,
+ 0,1,0,y,
+ 0,0,1,z,
+ 0,0,0,1);
+
+ mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].mult_right(trans_mat);
+ mMatHash[mMatrixMode]++;
+ }
+}
+
+void LLRender::scalef(const GLfloat& x, const GLfloat& y, const GLfloat& z)
+{
+ flush();
+
+ {
+ glh::matrix4f scale_mat(x,0,0,0,
+ 0,y,0,0,
+ 0,0,z,0,
+ 0,0,0,1);
+
+ mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].mult_right(scale_mat);
+ mMatHash[mMatrixMode]++;
+ }
+}
+
+void LLRender::ortho(F32 left, F32 right, F32 bottom, F32 top, F32 zNear, F32 zFar)
+{
+ flush();
+
+ {
+
+ glh::matrix4f ortho_mat(2.f/(right-left),0,0, -(right+left)/(right-left),
+ 0,2.f/(top-bottom),0, -(top+bottom)/(top-bottom),
+ 0,0,-2.f/(zFar-zNear), -(zFar+zNear)/(zFar-zNear),
+ 0,0,0,1);
+
+ mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].mult_right(ortho_mat);
+ mMatHash[mMatrixMode]++;
+ }
+}
+
+void LLRender::rotatef(const GLfloat& a, const GLfloat& x, const GLfloat& y, const GLfloat& z)
+{
+ flush();
+
+ {
+ F32 r = a * DEG_TO_RAD;
+
+ F32 c = cosf(r);
+ F32 s = sinf(r);
+
+ F32 ic = 1.f-c;
+
+ glh::matrix4f rot_mat(x*x*ic+c, x*y*ic-z*s, x*z*ic+y*s, 0,
+ x*y*ic+z*s, y*y*ic+c, y*z*ic-x*s, 0,
+ x*z*ic-y*s, y*z*ic+x*s, z*z*ic+c, 0,
+ 0,0,0,1);
+
+ mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].mult_right(rot_mat);
+ mMatHash[mMatrixMode]++;
+ }
+}
+
+void LLRender::pushMatrix()
+{
+ flush();
+
+ {
+ if (mMatIdx[mMatrixMode] < LL_MATRIX_STACK_DEPTH-1)
+ {
+ mMatrix[mMatrixMode][mMatIdx[mMatrixMode]+1] = mMatrix[mMatrixMode][mMatIdx[mMatrixMode]];
+ ++mMatIdx[mMatrixMode];
+ }
+ else
+ {
+ LL_WARNS() << "Matrix stack overflow." << LL_ENDL;
+ }
+ }
+}
+
+void LLRender::popMatrix()
+{
+ flush();
+ {
+ if (mMatIdx[mMatrixMode] > 0)
+ {
+ --mMatIdx[mMatrixMode];
+ mMatHash[mMatrixMode]++;
+ }
+ else
+ {
+ LL_WARNS() << "Matrix stack underflow." << LL_ENDL;
+ }
+ }
+}
+
+void LLRender::loadMatrix(const GLfloat* m)
+{
+ flush();
+ {
+ mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].set_value((GLfloat*) m);
+ mMatHash[mMatrixMode]++;
+ }
+}
+
+void LLRender::multMatrix(const GLfloat* m)
+{
+ flush();
+ {
+ glh::matrix4f mat((GLfloat*) m);
+
+ mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].mult_right(mat);
+ mMatHash[mMatrixMode]++;
+ }
+}
+
+void LLRender::matrixMode(eMatrixMode mode)
+{
+ if (mode == MM_TEXTURE)
+ {
+ U32 tex_index = gGL.getCurrentTexUnitIndex();
+ // the shaders don't actually reference anything beyond texture_matrix0/1 outside of terrain rendering
+ llassert(tex_index <= 3);
+ mode = eMatrixMode(MM_TEXTURE0 + tex_index);
+ if (mode > MM_TEXTURE3)
+ {
+ // getCurrentTexUnitIndex() can go as high as 32 (LL_NUM_TEXTURE_LAYERS)
+ // Large value will result in a crash at mMatrix
+ LL_WARNS_ONCE() << "Attempted to assign matrix mode out of bounds: " << mode << LL_ENDL;
+ mode = MM_TEXTURE0;
+ }
+ }
+
+ mMatrixMode = mode;
+}
+
+LLRender::eMatrixMode LLRender::getMatrixMode()
+{
+ if (mMatrixMode >= MM_TEXTURE0 && mMatrixMode <= MM_TEXTURE3)
+ { //always return MM_TEXTURE if current matrix mode points at any texture matrix
+ return MM_TEXTURE;
+ }
+
+ return mMatrixMode;
+}
+
+
+void LLRender::loadIdentity()
+{
+ flush();
+
+ {
+ llassert_always(mMatrixMode < NUM_MATRIX_MODES) ;
+
+ mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].make_identity();
+ mMatHash[mMatrixMode]++;
+ }
+}
+
+const glh::matrix4f& LLRender::getModelviewMatrix()
+{
+ return mMatrix[MM_MODELVIEW][mMatIdx[MM_MODELVIEW]];
+}
+
+const glh::matrix4f& LLRender::getProjectionMatrix()
+{
+ return mMatrix[MM_PROJECTION][mMatIdx[MM_PROJECTION]];
+}
+
+void LLRender::translateUI(F32 x, F32 y, F32 z)
+{
+ if (mUIOffset.empty())
+ {
+ LL_ERRS() << "Need to push a UI translation frame before offsetting" << LL_ENDL;
+ }
+
+ 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())
+ {
+ LL_ERRS() << "Need to push a UI transformation frame before scaling." << LL_ENDL;
+ }
+
+ 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())
+ {
+ LL_ERRS() << "UI offset stack blown." << LL_ENDL;
+ }
+ mUIOffset.pop_back();
+ mUIScale.pop_back();
+}
+
+LLVector3 LLRender::getUITranslation()
+{
+ if (mUIOffset.empty())
+ {
+ return LLVector3(0,0,0);
+ }
+ return mUIOffset.back();
+}
+
+LLVector3 LLRender::getUIScale()
+{
+ if (mUIScale.empty())
+ {
+ return LLVector3(1,1,1);
+ }
+ return mUIScale.back();
+}
+
+
+void LLRender::loadUIIdentity()
+{
+ if (mUIOffset.empty())
+ {
+ LL_ERRS() << "Need to push UI translation frame before clearing offset." << LL_ENDL;
+ }
+ 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();
+
+ if (mCurrColorMask[0] != writeColorR ||
+ mCurrColorMask[1] != writeColorG ||
+ mCurrColorMask[2] != writeColorB ||
+ mCurrColorMask[3] != writeAlpha)
+ {
+ 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:
+ LL_ERRS() << "Unknown Scene Blend Type: " << type << LL_ENDL;
+ break;
+ }
+}
+
+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 (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();
+
+ glBlendFuncSeparate(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
+ {
+ LL_DEBUGS() << "Non-existing texture unit layer requested: " << index << LL_ENDL;
+ return &mDummyTexUnit;
+ }
+}
+
+LLLightState* LLRender::getLight(U32 index)
+{
+ if (index < mLightState.size())
+ {
+ return &mLightState[index];
+ }
+
+ return NULL;
+}
+
+void LLRender::setAmbientLightColor(const LLColor4& color)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE
+ if (color != mAmbientLightColor)
+ {
+ ++mLightHash;
+ mAmbientLightColor = color;
+ }
+}
+
+bool LLRender::verifyTexUnitActive(U32 unitToVerify)
+{
+ if (mCurrTextureUnitIndex == unitToVerify)
+ {
+ return true;
+ }
+ else
+ {
+ LL_WARNS() << "TexUnit currently active: " << mCurrTextureUnitIndex << " (expecting " << unitToVerify << ")" << LL_ENDL;
+ return false;
+ }
+}
+
+void LLRender::clearErrors()
+{
+ while (glGetError())
+ {
+ //loop until no more error flags left
+ }
+}
+
+void LLRender::begin(const GLuint& mode)
+{
+ if (mode != mMode)
+ {
+ if (mode == LLRender::QUADS)
+ {
+ mQuadCycle = 1;
+ }
+
+ if (mMode == LLRender::QUADS ||
+ mMode == LLRender::LINES ||
+ mMode == LLRender::TRIANGLES ||
+ mMode == LLRender::POINTS)
+ {
+ flush();
+ }
+ else if (mCount != 0)
+ {
+ LL_ERRS() << "gGL.begin() called redundantly." << LL_ENDL;
+ }
+
+ mMode = mode;
+ }
+}
+
+void LLRender::end()
+{
+ if (mCount == 0)
+ {
+ return;
+ //IMM_ERRS << "GL begin and end called with no vertices specified." << LL_ENDL;
+ }
+
+ if ((mMode != LLRender::QUADS &&
+ mMode != LLRender::LINES &&
+ mMode != LLRender::TRIANGLES &&
+ mMode != LLRender::POINTS) ||
+ mCount > 2048)
+ {
+ flush();
+ }
+}
+void LLRender::flush()
+{
+ if (mCount > 0)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE;
+ llassert(LLGLSLShader::sCurBoundShaderPtr != nullptr);
+ if (!mUIOffset.empty())
+ {
+ sUICalls++;
+ sUIVerts += mCount;
+ }
+
+ //store mCount in a local variable to avoid re-entrance (drawArrays may call flush)
+ U32 count = mCount;
+
+ if (mMode == LLRender::QUADS && !sGLCoreProfile)
+ {
+ if (mCount%4 != 0)
+ {
+ count -= (mCount % 4);
+ LL_WARNS() << "Incomplete quad requested." << LL_ENDL;
+ }
+ }
+
+ if (mMode == LLRender::TRIANGLES)
+ {
+ if (mCount%3 != 0)
+ {
+ count -= (mCount % 3);
+ LL_WARNS() << "Incomplete triangle requested." << LL_ENDL;
+ }
+ }
+
+ if (mMode == LLRender::LINES)
+ {
+ if (mCount%2 != 0)
+ {
+ count -= (mCount % 2);
+ LL_WARNS() << "Incomplete line requested." << LL_ENDL;
+ }
+ }
+
+ mCount = 0;
+
+ if (mBuffer)
+ {
+
+ HBXXH64 hash;
+ U32 attribute_mask = LLGLSLShader::sCurBoundShaderPtr->mAttributeMask;
+
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb cache hash");
+
+ hash.update((U8*)mVerticesp.get(), count * sizeof(LLVector4a));
+ if (attribute_mask & LLVertexBuffer::MAP_TEXCOORD0)
+ {
+ hash.update((U8*)mTexcoordsp.get(), count * sizeof(LLVector2));
+ }
+
+ if (attribute_mask & LLVertexBuffer::MAP_COLOR)
+ {
+ hash.update((U8*)mColorsp.get(), count * sizeof(LLColor4U));
+ }
+
+ hash.finalize();
+ }
+
+
+ U64 vhash = hash.digest();
+
+ // check the VB cache before making a new vertex buffer
+ // This is a giant hack to deal with (mostly) our terrible UI rendering code
+ // that was built on top of OpenGL immediate mode. Huge performance wins
+ // can be had by not uploading geometry to VRAM unless absolutely necessary.
+ // Most of our usage of the "immediate mode" style draw calls is actually
+ // sending the same geometry over and over again.
+ // To leverage this, we maintain a running hash of the vertex stream being
+ // built up before a flush, and then check that hash against a VB
+ // cache just before creating a vertex buffer in VRAM
+ std::unordered_map<U64, LLVBCache>::iterator cache = sVBCache.find(vhash);
+
+ LLPointer<LLVertexBuffer> vb;
+
+ if (cache != sVBCache.end())
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb cache hit");
+ // cache hit, just use the cached buffer
+ vb = cache->second.vb;
+ cache->second.touched = std::chrono::steady_clock::now();
+ }
+ else
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb cache miss");
+ vb = new LLVertexBuffer(attribute_mask);
+ vb->allocateBuffer(count, 0);
+
+ vb->setBuffer();
+
+ vb->setPositionData((LLVector4a*) mVerticesp.get());
+
+ if (attribute_mask & LLVertexBuffer::MAP_TEXCOORD0)
+ {
+ vb->setTexCoordData(mTexcoordsp.get());
+ }
+
+ if (attribute_mask & LLVertexBuffer::MAP_COLOR)
+ {
+ vb->setColorData(mColorsp.get());
+ }
+
+ vb->unbind();
+
+ sVBCache[vhash] = { vb , std::chrono::steady_clock::now() };
+
+ static U32 miss_count = 0;
+ miss_count++;
+ if (miss_count > 1024)
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb cache clean");
+ miss_count = 0;
+ auto now = std::chrono::steady_clock::now();
+
+ using namespace std::chrono_literals;
+ // every 1024 misses, clean the cache of any VBs that haven't been touched in the last second
+ for (std::unordered_map<U64, LLVBCache>::iterator iter = sVBCache.begin(); iter != sVBCache.end(); )
+ {
+ if (now - iter->second.touched > 1s)
+ {
+ iter = sVBCache.erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+ }
+ }
+
+ vb->setBuffer();
+
+ if (mMode == LLRender::QUADS && sGLCoreProfile)
+ {
+ vb->drawArrays(LLRender::TRIANGLES, 0, count);
+ mQuadCycle = 1;
+ }
+ else
+ {
+ vb->drawArrays(mMode, 0, count);
+ }
+ }
+ else
+ {
+ // mBuffer is present in main thread and not present in an image thread
+ LL_ERRS() << "A flush call from outside main rendering thread" << LL_ENDL;
+ }
+
+
+ mVerticesp[0] = mVerticesp[count];
+ mTexcoordsp[0] = mTexcoordsp[count];
+ mColorsp[0] = mColorsp[count];
+
+ 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 > 2048)
+ { //break when buffer gets reasonably full to keep GL command buffers happy and avoid overflow below
+ switch (mMode)
+ {
+ case LLRender::POINTS: flush(); break;
+ case LLRender::TRIANGLES: if (mCount%3==0) flush(); break;
+ case LLRender::QUADS: if(mCount%4 == 0) flush(); break;
+ case LLRender::LINES: if (mCount%2 == 0) flush(); break;
+ }
+ }
+
+ if (mCount > 4094)
+ {
+ // LL_WARNS() << "GL immediate mode overflow. Some geometry not drawn." << LL_ENDL;
+ 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;
+ }
+
+ if (mMode == LLRender::QUADS && LLRender::sGLCoreProfile)
+ {
+ mQuadCycle++;
+ if (mQuadCycle == 4)
+ { //copy two vertices so fourth quad element will add a triangle
+ mQuadCycle = 0;
+
+ mCount++;
+ mVerticesp[mCount] = mVerticesp[mCount-3];
+ mColorsp[mCount] = mColorsp[mCount-3];
+ mTexcoordsp[mCount] = mTexcoordsp[mCount-3];
+
+ mCount++;
+ mVerticesp[mCount] = mVerticesp[mCount-2];
+ mColorsp[mCount] = mColorsp[mCount-2];
+ mTexcoordsp[mCount] = mTexcoordsp[mCount-2];
+ }
+ }
+
+ 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)
+ {
+ // LL_WARNS() << "GL immediate mode overflow. Some geometry not drawn." << LL_ENDL;
+ return;
+ }
+
+ if (sGLCoreProfile && mMode == LLRender::QUADS)
+ { //quads are deprecated, convert to triangle list
+ S32 i = 0;
+
+ while (i < vert_count)
+ {
+ //read first three
+ mVerticesp[mCount++] = verts[i++];
+ mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
+ mColorsp[mCount] = mColorsp[mCount-1];
+
+ mVerticesp[mCount++] = verts[i++];
+ mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
+ mColorsp[mCount] = mColorsp[mCount-1];
+
+ mVerticesp[mCount++] = verts[i++];
+ mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
+ mColorsp[mCount] = mColorsp[mCount-1];
+
+ //copy two
+ mVerticesp[mCount++] = verts[i-3];
+ mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
+ mColorsp[mCount] = mColorsp[mCount-1];
+
+ mVerticesp[mCount++] = verts[i-1];
+ mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
+ mColorsp[mCount] = mColorsp[mCount-1];
+
+ //copy last one
+ mVerticesp[mCount++] = verts[i++];
+ mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
+ mColorsp[mCount] = mColorsp[mCount-1];
+ }
+ }
+ else
+ {
+ for (S32 i = 0; i < vert_count; i++)
+ {
+ mVerticesp[mCount] = verts[i];
+
+ mCount++;
+ mTexcoordsp[mCount] = mTexcoordsp[mCount-1];
+ mColorsp[mCount] = mColorsp[mCount-1];
+ }
+ }
+
+ if( mCount > 0 ) // ND: Guard against crashes if mCount is zero, yes it can happen
+ mVerticesp[mCount] = mVerticesp[mCount-1];
+}
+
+void LLRender::vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, S32 vert_count)
+{
+ if (mCount + vert_count > 4094)
+ {
+ // LL_WARNS() << "GL immediate mode overflow. Some geometry not drawn." << LL_ENDL;
+ return;
+ }
+
+ if (sGLCoreProfile && mMode == LLRender::QUADS)
+ { //quads are deprecated, convert to triangle list
+ S32 i = 0;
+
+ while (i < vert_count)
+ {
+ //read first three
+ mVerticesp[mCount] = verts[i];
+ mTexcoordsp[mCount++] = uvs[i++];
+ mColorsp[mCount] = mColorsp[mCount-1];
+
+ mVerticesp[mCount] = verts[i];
+ mTexcoordsp[mCount++] = uvs[i++];
+ mColorsp[mCount] = mColorsp[mCount-1];
+
+ mVerticesp[mCount] = verts[i];
+ mTexcoordsp[mCount++] = uvs[i++];
+ mColorsp[mCount] = mColorsp[mCount-1];
+
+ //copy last two
+ mVerticesp[mCount] = verts[i-3];
+ mTexcoordsp[mCount++] = uvs[i-3];
+ mColorsp[mCount] = mColorsp[mCount-1];
+
+ mVerticesp[mCount] = verts[i-1];
+ mTexcoordsp[mCount++] = uvs[i-1];
+ mColorsp[mCount] = mColorsp[mCount-1];
+
+ //copy last one
+ mVerticesp[mCount] = verts[i];
+ mTexcoordsp[mCount++] = uvs[i++];
+ mColorsp[mCount] = mColorsp[mCount-1];
+ }
+ }
+ else
+ {
+ for (S32 i = 0; i < vert_count; i++)
+ {
+ mVerticesp[mCount] = verts[i];
+ mTexcoordsp[mCount] = uvs[i];
+
+ mCount++;
+ mColorsp[mCount] = mColorsp[mCount-1];
+ }
+ }
+
+ if (mCount > 0)
+ {
+ 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)
+ {
+ // LL_WARNS() << "GL immediate mode overflow. Some geometry not drawn." << LL_ENDL;
+ return;
+ }
+
+
+ if (sGLCoreProfile && mMode == LLRender::QUADS)
+ { //quads are deprecated, convert to triangle list
+ S32 i = 0;
+
+ while (i < vert_count)
+ {
+ //read first three
+ mVerticesp[mCount] = verts[i];
+ mTexcoordsp[mCount] = uvs[i];
+ mColorsp[mCount++] = colors[i++];
+
+ mVerticesp[mCount] = verts[i];
+ mTexcoordsp[mCount] = uvs[i];
+ mColorsp[mCount++] = colors[i++];
+
+ mVerticesp[mCount] = verts[i];
+ mTexcoordsp[mCount] = uvs[i];
+ mColorsp[mCount++] = colors[i++];
+
+ //copy last two
+ mVerticesp[mCount] = verts[i-3];
+ mTexcoordsp[mCount] = uvs[i-3];
+ mColorsp[mCount++] = colors[i-3];
+
+ mVerticesp[mCount] = verts[i-1];
+ mTexcoordsp[mCount] = uvs[i-1];
+ mColorsp[mCount++] = colors[i-1];
+
+ //copy last one
+ mVerticesp[mCount] = verts[i];
+ mTexcoordsp[mCount] = uvs[i];
+ mColorsp[mCount++] = colors[i++];
+ }
+ }
+ else
+ {
+ for (S32 i = 0; i < vert_count; i++)
+ {
+ mVerticesp[mCount] = verts[i];
+ mTexcoordsp[mCount] = uvs[i];
+ mColorsp[mCount] = colors[i];
+
+ mCount++;
+ }
+ }
+
+ if (mCount > 0)
+ {
+ 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)
+{
+ if (!LLGLSLShader::sCurBoundShaderPtr || LLGLSLShader::sCurBoundShaderPtr->mAttributeMask & LLVertexBuffer::MAP_COLOR)
+ {
+ mColorsp[mCount] = LLColor4U(r,g,b,a);
+ }
+ else
+ { //not using shaders or shader reads color from a uniform
+ diffuseColor4ub(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::diffuseColor3f(F32 r, F32 g, F32 b)
+{
+ LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+ llassert(shader != NULL);
+
+ if (shader)
+ {
+ shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, r,g,b,1.f);
+ }
+ else
+ {
+ glColor3f(r,g,b);
+ }
+}
+
+void LLRender::diffuseColor3fv(const F32* c)
+{
+ LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+ llassert(shader != NULL);
+
+ if (shader)
+ {
+ shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, c[0], c[1], c[2], 1.f);
+ }
+ else
+ {
+ glColor3fv(c);
+ }
+}
+
+void LLRender::diffuseColor4f(F32 r, F32 g, F32 b, F32 a)
+{
+ LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+ llassert(shader != NULL);
+
+ if (shader)
+ {
+ shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, r,g,b,a);
+ }
+ else
+ {
+ glColor4f(r,g,b,a);
+ }
+}
+
+void LLRender::diffuseColor4fv(const F32* c)
+{
+ LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+ llassert(shader != NULL);
+
+ if (shader)
+ {
+ shader->uniform4fv(LLShaderMgr::DIFFUSE_COLOR, 1, c);
+ }
+ else
+ {
+ glColor4fv(c);
+ }
+}
+
+void LLRender::diffuseColor4ubv(const U8* c)
+{
+ LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+ llassert(shader != NULL);
+
+ if (shader)
+ {
+ shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, c[0]/255.f, c[1]/255.f, c[2]/255.f, c[3]/255.f);
+ }
+ else
+ {
+ glColor4ubv(c);
+ }
+}
+
+void LLRender::diffuseColor4ub(U8 r, U8 g, U8 b, U8 a)
+{
+ LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+ llassert(shader != NULL);
+
+ if (shader)
+ {
+ shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, r/255.f, g/255.f, b/255.f, a/255.f);
+ }
+ else
+ {
+ glColor4ub(r,g,b,a);
+ }
+}
+
+
+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;
+}
+
+
+
+glh::matrix4f copy_matrix(F32* src)
+{
+ glh::matrix4f ret;
+ ret.set_value(src);
+ return ret;
+}
+
+glh::matrix4f get_current_modelview()
+{
+ return copy_matrix(gGLModelView);
+}
+
+glh::matrix4f get_current_projection()
+{
+ return copy_matrix(gGLProjection);
+}
+
+glh::matrix4f get_last_modelview()
+{
+ return copy_matrix(gGLLastModelView);
+}
+
+glh::matrix4f get_last_projection()
+{
+ return copy_matrix(gGLLastProjection);
+}
+
+void copy_matrix(const glh::matrix4f& src, F32* dst)
+{
+ for (U32 i = 0; i < 16; i++)
+ {
+ dst[i] = src.m[i];
+ }
+}
+
+void set_current_modelview(const glh::matrix4f& mat)
+{
+ copy_matrix(mat, gGLModelView);
+}
+
+void set_current_projection(glh::matrix4f& mat)
+{
+ copy_matrix(mat, gGLProjection);
+}
+
+glh::matrix4f gl_ortho(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat znear, GLfloat zfar)
+{
+ glh::matrix4f ret(
+ 2.f/(right-left), 0.f, 0.f, -(right+left)/(right-left),
+ 0.f, 2.f/(top-bottom), 0.f, -(top+bottom)/(top-bottom),
+ 0.f, 0.f, -2.f/(zfar-znear), -(zfar+znear)/(zfar-znear),
+ 0.f, 0.f, 0.f, 1.f);
+
+ return ret;
+}
+
+glh::matrix4f gl_perspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar)
+{
+ GLfloat f = 1.f/tanf(DEG_TO_RAD*fovy/2.f);
+
+ return glh::matrix4f(f/aspect, 0, 0, 0,
+ 0, f, 0, 0,
+ 0, 0, (zFar+zNear)/(zNear-zFar), (2.f*zFar*zNear)/(zNear-zFar),
+ 0, 0, -1.f, 0);
+}
+
+glh::matrix4f gl_lookat(LLVector3 eye, LLVector3 center, LLVector3 up)
+{
+ LLVector3 f = center-eye;
+ f.normVec();
+ up.normVec();
+ LLVector3 s = f % up;
+ LLVector3 u = s % f;
+
+ return glh::matrix4f(s[0], s[1], s[2], 0,
+ u[0], u[1], u[2], 0,
+ -f[0], -f[1], -f[2], 0,
+ 0, 0, 0, 1);
+
+}
diff --git a/indra/llrender/llrender2dutils.cpp b/indra/llrender/llrender2dutils.cpp
index 85db2fed66..ef02fbd071 100644
--- a/indra/llrender/llrender2dutils.cpp
+++ b/indra/llrender/llrender2dutils.cpp
@@ -1,1582 +1,1582 @@
-/**
- * @file llrender2dutils.cpp
- * @brief GL function implementations for immediate-mode gl drawing.
- *
- * $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"
-
-// Linden library includes
-#include "v2math.h"
-#include "m3math.h"
-#include "v4color.h"
-#include "llfontgl.h"
-#include "llrender.h"
-#include "llrect.h"
-#include "llgl.h"
-#include "lltexture.h"
-#include "llfasttimer.h"
-
-// Project includes
-#include "llrender2dutils.h"
-#include "lluiimage.h"
-
-
-//
-// Globals
-//
-const LLColor4 UI_VERTEX_COLOR(1.f, 1.f, 1.f, 1.f);
-
-//
-// Functions
-//
-
-bool ui_point_in_rect(S32 x, S32 y, S32 left, S32 top, S32 right, S32 bottom)
-{
- if (x < left || right < x) return false;
- if (y < bottom || top < y) return false;
- return true;
-}
-
-
-// Puts GL into 2D drawing mode by turning off lighting, setting to an
-// orthographic projection, etc.
-void gl_state_for_2d(S32 width, S32 height)
-{
- stop_glerror();
- F32 window_width = (F32) width;//gViewerWindow->getWindowWidth();
- F32 window_height = (F32) height;//gViewerWindow->getWindowHeight();
-
- gGL.matrixMode(LLRender::MM_PROJECTION);
- gGL.loadIdentity();
- gGL.ortho(0.0f, llmax(window_width, 1.f), 0.0f, llmax(window_height,1.f), -1.0f, 1.0f);
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- gGL.loadIdentity();
- stop_glerror();
-}
-
-
-void gl_draw_x(const LLRect& rect, const LLColor4& color)
-{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.color4fv( color.mV );
-
- gGL.begin( LLRender::LINES );
- gGL.vertex2i( rect.mLeft, rect.mTop );
- gGL.vertex2i( rect.mRight, rect.mBottom );
- gGL.vertex2i( rect.mLeft, rect.mBottom );
- gGL.vertex2i( rect.mRight, rect.mTop );
- gGL.end();
-}
-
-
-void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, S32 pixel_offset, bool filled)
-{
- gGL.color4fv(color.mV);
- gl_rect_2d_offset_local(left, top, right, bottom, pixel_offset, filled);
-}
-
-void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixel_offset, bool filled)
-{
- gGL.pushUIMatrix();
- left += LLFontGL::sCurOrigin.mX;
- right += LLFontGL::sCurOrigin.mX;
- bottom += LLFontGL::sCurOrigin.mY;
- top += LLFontGL::sCurOrigin.mY;
-
- gGL.loadUIIdentity();
- gl_rect_2d(llfloor((F32)left * LLRender::sUIGLScaleFactor.mV[VX]) - pixel_offset,
- llfloor((F32)top * LLRender::sUIGLScaleFactor.mV[VY]) + pixel_offset,
- llfloor((F32)right * LLRender::sUIGLScaleFactor.mV[VX]) + pixel_offset,
- llfloor((F32)bottom * LLRender::sUIGLScaleFactor.mV[VY]) - pixel_offset,
- filled);
- gGL.popUIMatrix();
-}
-
-
-void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, bool filled )
-{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- // Counterclockwise quad will face the viewer
- if( filled )
- {
- gGL.begin( LLRender::QUADS );
- gGL.vertex2i(left, top);
- gGL.vertex2i(left, bottom);
- gGL.vertex2i(right, bottom);
- gGL.vertex2i(right, top);
- gGL.end();
- }
- else
- {
- top--;
- right--;
- gGL.begin( LLRender::LINE_STRIP );
- gGL.vertex2i(left, top);
- gGL.vertex2i(left, bottom);
- gGL.vertex2i(right, bottom);
- gGL.vertex2i(right, top);
- gGL.vertex2i(left, top);
- gGL.end();
- }
-}
-
-void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, bool filled )
-{
- gGL.color4fv( color.mV );
- gl_rect_2d( left, top, right, bottom, filled );
-}
-
-
-void gl_rect_2d( const LLRect& rect, const LLColor4& color, bool filled )
-{
- gGL.color4fv( color.mV );
- gl_rect_2d( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, filled );
-}
-
-// Given a rectangle on the screen, draws a drop shadow _outside_
-// the right and bottom edges of it. Along the right it has width "lines"
-// and along the bottom it has height "lines".
-void gl_drop_shadow(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &start_color, S32 lines)
-{
- stop_glerror();
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- // HACK: Overlap with the rectangle by a single pixel.
- right--;
- bottom++;
- lines++;
-
- LLColor4 end_color = start_color;
- end_color.mV[VALPHA] = 0.f;
-
- gGL.begin(LLRender::QUADS);
-
- // Right edge, CCW faces screen
- gGL.color4fv(start_color.mV);
- gGL.vertex2i(right, top-lines);
- gGL.vertex2i(right, bottom);
- gGL.color4fv(end_color.mV);
- gGL.vertex2i(right+lines, bottom);
- gGL.vertex2i(right+lines, top-lines);
-
- // Bottom edge, CCW faces screen
- gGL.color4fv(start_color.mV);
- gGL.vertex2i(right, bottom);
- gGL.vertex2i(left+lines, bottom);
- gGL.color4fv(end_color.mV);
- gGL.vertex2i(left+lines, bottom-lines);
- gGL.vertex2i(right, bottom-lines);
-
- // bottom left Corner
- gGL.color4fv(start_color.mV);
- gGL.vertex2i(left+lines, bottom);
- gGL.color4fv(end_color.mV);
- gGL.vertex2i(left, bottom);
- // make the bottom left corner not sharp
- gGL.vertex2i(left+1, bottom-lines+1);
- gGL.vertex2i(left+lines, bottom-lines);
-
- // bottom right corner
- gGL.color4fv(start_color.mV);
- gGL.vertex2i(right, bottom);
- gGL.color4fv(end_color.mV);
- gGL.vertex2i(right, bottom-lines);
- // make the rightmost corner not sharp
- gGL.vertex2i(right+lines-1, bottom-lines+1);
- gGL.vertex2i(right+lines, bottom);
-
- // top right corner
- gGL.color4fv(start_color.mV);
- gGL.vertex2i( right, top-lines );
- gGL.color4fv(end_color.mV);
- gGL.vertex2i( right+lines, top-lines );
- // make the corner not sharp
- gGL.vertex2i( right+lines-1, top-1 );
- gGL.vertex2i( right, top );
-
- gGL.end();
- stop_glerror();
-}
-
-void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2 )
-{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.begin(LLRender::LINES);
- gGL.vertex2i(x1, y1);
- gGL.vertex2i(x2, y2);
- gGL.end();
-}
-
-void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2, const LLColor4 &color )
-{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.color4fv( color.mV );
-
- gGL.begin(LLRender::LINES);
- gGL.vertex2i(x1, y1);
- gGL.vertex2i(x2, y2);
- gGL.end();
-}
-
-void gl_triangle_2d(S32 x1, S32 y1, S32 x2, S32 y2, S32 x3, S32 y3, const LLColor4& color, bool filled)
-{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.color4fv(color.mV);
-
- if (filled)
- {
- gGL.begin(LLRender::TRIANGLES);
- }
- else
- {
- gGL.begin(LLRender::LINE_LOOP);
- }
- gGL.vertex2i(x1, y1);
- gGL.vertex2i(x2, y2);
- gGL.vertex2i(x3, y3);
- gGL.end();
-}
-
-void gl_corners_2d(S32 left, S32 top, S32 right, S32 bottom, S32 length, F32 max_frac)
-{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- length = llmin((S32)(max_frac*(right - left)), length);
- length = llmin((S32)(max_frac*(top - bottom)), length);
- gGL.begin(LLRender::LINES);
- gGL.vertex2i(left, top);
- gGL.vertex2i(left + length, top);
-
- gGL.vertex2i(left, top);
- gGL.vertex2i(left, top - length);
-
- gGL.vertex2i(left, bottom);
- gGL.vertex2i(left + length, bottom);
-
- gGL.vertex2i(left, bottom);
- gGL.vertex2i(left, bottom + length);
-
- gGL.vertex2i(right, top);
- gGL.vertex2i(right - length, top);
-
- gGL.vertex2i(right, top);
- gGL.vertex2i(right, top - length);
-
- gGL.vertex2i(right, bottom);
- gGL.vertex2i(right - length, bottom);
-
- gGL.vertex2i(right, bottom);
- gGL.vertex2i(right, bottom + length);
- gGL.end();
-}
-
-
-void gl_draw_image( S32 x, S32 y, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect )
-{
- if (NULL == image)
- {
- LL_WARNS() << "image == NULL; aborting function" << LL_ENDL;
- return;
- }
- gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), 0.f, image, color, uv_rect );
-}
-
-void gl_draw_scaled_target(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* target, const LLColor4& color, const LLRectf& uv_rect)
-{
- gl_draw_scaled_rotated_image(x, y, width, height, 0.f, NULL, color, uv_rect, target);
-}
-
-void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect)
-{
- if (NULL == image)
- {
- LL_WARNS() << "image == NULL; aborting function" << LL_ENDL;
- return;
- }
- gl_draw_scaled_rotated_image( x, y, width, height, 0.f, image, color, uv_rect );
-}
-
-void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLTexture* image, const LLColor4& color, bool solid_color, const LLRectf& uv_rect, bool scale_inner)
-{
- if (NULL == image)
- {
- LL_WARNS() << "image == NULL; aborting function" << LL_ENDL;
- return;
- }
-
- // scale screen size of borders down
- F32 border_width_fraction = (F32)border_width / (F32)image->getWidth(0);
- F32 border_height_fraction = (F32)border_height / (F32)image->getHeight(0);
-
- LLRectf scale_rect(border_width_fraction, 1.f - border_height_fraction, 1.f - border_width_fraction, border_height_fraction);
- gl_draw_scaled_image_with_border(x, y, width, height, image, color, solid_color, uv_rect, scale_rect, scale_inner);
-}
-
-void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTexture* image, const LLColor4& color, bool solid_color, const LLRectf& uv_outer_rect, const LLRectf& center_rect, bool scale_inner)
-{
- stop_glerror();
-
- if (NULL == image)
- {
- LL_WARNS() << "image == NULL; aborting function" << LL_ENDL;
- return;
- }
-
- if (solid_color)
- {
- gSolidColorProgram.bind();
- }
-
- if (center_rect.mLeft == 0.f
- && center_rect.mRight == 1.f
- && center_rect.mBottom == 0.f
- && center_rect.mTop == 1.f)
- {
- gl_draw_scaled_image(x, y, width, height, image, color, uv_outer_rect);
- }
- else
- {
- // add in offset of current image to current UI translation
- const LLVector3 ui_scale = gGL.getUIScale();
- const LLVector3 ui_translation = (gGL.getUITranslation() + LLVector3(x, y, 0.f)).scaledVec(ui_scale);
-
- F32 uv_width = uv_outer_rect.getWidth();
- F32 uv_height = uv_outer_rect.getHeight();
-
- // shrink scaling region to be proportional to clipped image region
- LLRectf uv_center_rect( uv_outer_rect.mLeft + (center_rect.mLeft * uv_width),
- uv_outer_rect.mBottom + (center_rect.mTop * uv_height),
- uv_outer_rect.mLeft + (center_rect.mRight * uv_width),
- uv_outer_rect.mBottom + (center_rect.mBottom * uv_height));
-
- F32 image_width = image->getWidth(0);
- F32 image_height = image->getHeight(0);
-
- S32 image_natural_width = ll_round(image_width * uv_width);
- S32 image_natural_height = ll_round(image_height * uv_height);
-
- LLRectf draw_center_rect( uv_center_rect.mLeft * image_width,
- uv_center_rect.mTop * image_height,
- uv_center_rect.mRight * image_width,
- uv_center_rect.mBottom * image_height);
-
- if (scale_inner)
- {
- // scale center region of image to drawn region
- draw_center_rect.mRight += width - image_natural_width;
- draw_center_rect.mTop += height - image_natural_height;
-
- const F32 border_shrink_width = llmax(0.f, draw_center_rect.mLeft - draw_center_rect.mRight);
- const F32 border_shrink_height = llmax(0.f, draw_center_rect.mBottom - draw_center_rect.mTop);
-
- const F32 shrink_width_ratio = center_rect.getWidth() == 1.f ? 0.f : border_shrink_width / ((F32)image_natural_width * (1.f - center_rect.getWidth()));
- const F32 shrink_height_ratio = center_rect.getHeight() == 1.f ? 0.f : border_shrink_height / ((F32)image_natural_height * (1.f - center_rect.getHeight()));
-
- const F32 border_shrink_scale = 1.f - llmax(shrink_width_ratio, shrink_height_ratio);
- draw_center_rect.mLeft *= border_shrink_scale;
- draw_center_rect.mTop = lerp((F32)height, (F32)draw_center_rect.mTop, border_shrink_scale);
- draw_center_rect.mRight = lerp((F32)width, (F32)draw_center_rect.mRight, border_shrink_scale);
- draw_center_rect.mBottom *= border_shrink_scale;
- }
- else
- {
- // keep center region of image at fixed scale, but in same relative position
- F32 scale_factor = llmin((F32)width / draw_center_rect.getWidth(), (F32)height / draw_center_rect.getHeight(), 1.f);
- F32 scaled_width = draw_center_rect.getWidth() * scale_factor;
- F32 scaled_height = draw_center_rect.getHeight() * scale_factor;
- draw_center_rect.setCenterAndSize(uv_center_rect.getCenterX() * width, uv_center_rect.getCenterY() * height, scaled_width, scaled_height);
- }
-
- draw_center_rect.mLeft = ll_round(ui_translation.mV[VX] + (F32)draw_center_rect.mLeft * ui_scale.mV[VX]);
- draw_center_rect.mTop = ll_round(ui_translation.mV[VY] + (F32)draw_center_rect.mTop * ui_scale.mV[VY]);
- draw_center_rect.mRight = ll_round(ui_translation.mV[VX] + (F32)draw_center_rect.mRight * ui_scale.mV[VX]);
- draw_center_rect.mBottom = ll_round(ui_translation.mV[VY] + (F32)draw_center_rect.mBottom * ui_scale.mV[VY]);
-
- LLRectf draw_outer_rect(ui_translation.mV[VX],
- ui_translation.mV[VY] + height * ui_scale.mV[VY],
- ui_translation.mV[VX] + width * ui_scale.mV[VX],
- ui_translation.mV[VY]);
-
- LLGLSUIDefault gls_ui;
-
- gGL.getTexUnit(0)->bind(image, true);
-
- gGL.color4fv(color.mV);
-
- const S32 NUM_VERTICES = 9 * 4; // 9 quads
- LLVector2 uv[NUM_VERTICES];
- LLVector3 pos[NUM_VERTICES];
-
- S32 index = 0;
-
- gGL.begin(LLRender::QUADS);
- {
- // draw bottom left
- uv[index] = LLVector2(uv_outer_rect.mLeft, uv_outer_rect.mBottom);
- pos[index] = LLVector3(draw_outer_rect.mLeft, draw_outer_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mLeft, uv_outer_rect.mBottom);
- pos[index] = LLVector3(draw_center_rect.mLeft, draw_outer_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mBottom);
- pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_outer_rect.mLeft, uv_center_rect.mBottom);
- pos[index] = LLVector3(draw_outer_rect.mLeft, draw_center_rect.mBottom, 0.f);
- index++;
-
- // draw bottom middle
- uv[index] = LLVector2(uv_center_rect.mLeft, uv_outer_rect.mBottom);
- pos[index] = LLVector3(draw_center_rect.mLeft, draw_outer_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mRight, uv_outer_rect.mBottom);
- pos[index] = LLVector3(draw_center_rect.mRight, draw_outer_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mBottom);
- pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mBottom);
- pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mBottom, 0.f);
- index++;
-
- // draw bottom right
- uv[index] = LLVector2(uv_center_rect.mRight, uv_outer_rect.mBottom);
- pos[index] = LLVector3(draw_center_rect.mRight, draw_outer_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_outer_rect.mRight, uv_outer_rect.mBottom);
- pos[index] = LLVector3(draw_outer_rect.mRight, draw_outer_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_outer_rect.mRight, uv_center_rect.mBottom);
- pos[index] = LLVector3(draw_outer_rect.mRight, draw_center_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mBottom);
- pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mBottom, 0.f);
- index++;
-
- // draw left
- uv[index] = LLVector2(uv_outer_rect.mLeft, uv_center_rect.mBottom);
- pos[index] = LLVector3(draw_outer_rect.mLeft, draw_center_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mBottom);
- pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mTop);
- pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mTop, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_outer_rect.mLeft, uv_center_rect.mTop);
- pos[index] = LLVector3(draw_outer_rect.mLeft, draw_center_rect.mTop, 0.f);
- index++;
-
- // draw middle
- uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mBottom);
- pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mBottom);
- pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mTop);
- pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mTop, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mTop);
- pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mTop, 0.f);
- index++;
-
- // draw right
- uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mBottom);
- pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_outer_rect.mRight, uv_center_rect.mBottom);
- pos[index] = LLVector3(draw_outer_rect.mRight, draw_center_rect.mBottom, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_outer_rect.mRight, uv_center_rect.mTop);
- pos[index] = LLVector3(draw_outer_rect.mRight, draw_center_rect.mTop, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mTop);
- pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mTop, 0.f);
- index++;
-
- // draw top left
- uv[index] = LLVector2(uv_outer_rect.mLeft, uv_center_rect.mTop);
- pos[index] = LLVector3(draw_outer_rect.mLeft, draw_center_rect.mTop, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mTop);
- pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mTop, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mLeft, uv_outer_rect.mTop);
- pos[index] = LLVector3(draw_center_rect.mLeft, draw_outer_rect.mTop, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_outer_rect.mLeft, uv_outer_rect.mTop);
- pos[index] = LLVector3(draw_outer_rect.mLeft, draw_outer_rect.mTop, 0.f);
- index++;
-
- // draw top middle
- uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mTop);
- pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mTop, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mTop);
- pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mTop, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mRight, uv_outer_rect.mTop);
- pos[index] = LLVector3(draw_center_rect.mRight, draw_outer_rect.mTop, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mLeft, uv_outer_rect.mTop);
- pos[index] = LLVector3(draw_center_rect.mLeft, draw_outer_rect.mTop, 0.f);
- index++;
-
- // draw top right
- uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mTop);
- pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mTop, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_outer_rect.mRight, uv_center_rect.mTop);
- pos[index] = LLVector3(draw_outer_rect.mRight, draw_center_rect.mTop, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_outer_rect.mRight, uv_outer_rect.mTop);
- pos[index] = LLVector3(draw_outer_rect.mRight, draw_outer_rect.mTop, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_center_rect.mRight, uv_outer_rect.mTop);
- pos[index] = LLVector3(draw_center_rect.mRight, draw_outer_rect.mTop, 0.f);
- index++;
-
- gGL.vertexBatchPreTransformed(pos, uv, NUM_VERTICES);
- }
- gGL.end();
- }
-
- if (solid_color)
- {
- gUIProgram.bind();
- }
-}
-
-void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect)
-{
- gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), degrees, image, color, uv_rect );
-}
-
-void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect, LLRenderTarget* target)
-{
- if (!image && !target)
- {
- LL_WARNS() << "image == NULL; aborting function" << LL_ENDL;
- return;
- }
-
- LLGLSUIDefault gls_ui;
-
- if(image != NULL)
- {
- gGL.getTexUnit(0)->bind(image, true);
- }
- else
- {
- gGL.getTexUnit(0)->bind(target);
- }
-
- gGL.color4fv(color.mV);
-
- if (degrees == 0.f)
- {
- const S32 NUM_VERTICES = 4; // 9 quads
- LLVector2 uv[NUM_VERTICES];
- LLVector3 pos[NUM_VERTICES];
-
- gGL.begin(LLRender::QUADS);
- {
- LLVector3 ui_scale = gGL.getUIScale();
- LLVector3 ui_translation = gGL.getUITranslation();
- ui_translation.mV[VX] += x;
- ui_translation.mV[VY] += y;
- ui_translation.scaleVec(ui_scale);
- S32 index = 0;
- S32 scaled_width = ll_round(width * ui_scale.mV[VX]);
- S32 scaled_height = ll_round(height * ui_scale.mV[VY]);
-
- uv[index] = LLVector2(uv_rect.mRight, uv_rect.mTop);
- pos[index] = LLVector3(ui_translation.mV[VX] + scaled_width, ui_translation.mV[VY] + scaled_height, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_rect.mLeft, uv_rect.mTop);
- pos[index] = LLVector3(ui_translation.mV[VX], ui_translation.mV[VY] + scaled_height, 0.f);
- index++;
-
- uv[index] = LLVector2(uv_rect.mLeft, uv_rect.mBottom);
- pos[index] = LLVector3(ui_translation.mV[VX], ui_translation.mV[VY], 0.f);
- index++;
-
- uv[index] = LLVector2(uv_rect.mRight, uv_rect.mBottom);
- pos[index] = LLVector3(ui_translation.mV[VX] + scaled_width, ui_translation.mV[VY], 0.f);
- index++;
-
- gGL.vertexBatchPreTransformed(pos, uv, NUM_VERTICES);
- }
- gGL.end();
- }
- else
- {
- gGL.pushUIMatrix();
- gGL.translateUI((F32)x, (F32)y, 0.f);
-
- F32 offset_x = F32(width/2);
- F32 offset_y = F32(height/2);
-
- gGL.translateUI(offset_x, offset_y, 0.f);
-
- LLMatrix3 quat(0.f, 0.f, degrees*DEG_TO_RAD);
-
- if(image != NULL)
- {
- gGL.getTexUnit(0)->bind(image, true);
- }
- else
- {
- gGL.getTexUnit(0)->bind(target);
- }
-
- gGL.color4fv(color.mV);
-
- gGL.begin(LLRender::QUADS);
- {
- LLVector3 v;
-
- v = LLVector3(offset_x, offset_y, 0.f) * quat;
- gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop);
- gGL.vertex2f(v.mV[0], v.mV[1] );
-
- v = LLVector3(-offset_x, offset_y, 0.f) * quat;
- gGL.texCoord2f(uv_rect.mLeft, uv_rect.mTop);
- gGL.vertex2f(v.mV[0], v.mV[1] );
-
- v = LLVector3(-offset_x, -offset_y, 0.f) * quat;
- gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom);
- gGL.vertex2f(v.mV[0], v.mV[1] );
-
- v = LLVector3(offset_x, -offset_y, 0.f) * quat;
- gGL.texCoord2f(uv_rect.mRight, uv_rect.mBottom);
- gGL.vertex2f(v.mV[0], v.mV[1] );
- }
- gGL.end();
- gGL.popUIMatrix();
- }
-}
-
-void gl_line_3d( const LLVector3& start, const LLVector3& end, const LLColor4& color)
-{
- gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], color.mV[VALPHA]);
-
- gGL.flush();
- glLineWidth(2.5f);
-
- gGL.begin(LLRender::LINES);
- {
- gGL.vertex3fv( start.mV );
- gGL.vertex3fv( end.mV );
- }
- gGL.end();
-
- LLRender2D::getInstance()->setLineWidth(1.f);
-}
-
-void gl_arc_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, bool filled, F32 start_angle, F32 end_angle)
-{
- if (end_angle < start_angle)
- {
- end_angle += F_TWO_PI;
- }
-
- gGL.pushUIMatrix();
- {
- gGL.translateUI(center_x, center_y, 0.f);
-
- // Inexact, but reasonably fast.
- F32 delta = (end_angle - start_angle) / steps;
- F32 sin_delta = sin( delta );
- F32 cos_delta = cos( delta );
- F32 x = cosf(start_angle) * radius;
- F32 y = sinf(start_angle) * radius;
-
- if (filled)
- {
- gGL.begin(LLRender::TRIANGLE_FAN);
- gGL.vertex2f(0.f, 0.f);
- // make sure circle is complete
- steps += 1;
- }
- else
- {
- gGL.begin(LLRender::LINE_STRIP);
- }
-
- while( steps-- )
- {
- // Successive rotations
- gGL.vertex2f( x, y );
- F32 x_new = x * cos_delta - y * sin_delta;
- y = x * sin_delta + y * cos_delta;
- x = x_new;
- }
- gGL.end();
- }
- gGL.popUIMatrix();
-}
-
-void gl_circle_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, bool filled)
-{
- gGL.pushUIMatrix();
- {
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.translateUI(center_x, center_y, 0.f);
-
- // Inexact, but reasonably fast.
- F32 delta = F_TWO_PI / steps;
- F32 sin_delta = sin( delta );
- F32 cos_delta = cos( delta );
- F32 x = radius;
- F32 y = 0.f;
-
- if (filled)
- {
- gGL.begin(LLRender::TRIANGLE_FAN);
- gGL.vertex2f(0.f, 0.f);
- // make sure circle is complete
- steps += 1;
- }
- else
- {
- gGL.begin(LLRender::LINE_LOOP);
- }
-
- while( steps-- )
- {
- // Successive rotations
- gGL.vertex2f( x, y );
- F32 x_new = x * cos_delta - y * sin_delta;
- y = x * sin_delta + y * cos_delta;
- x = x_new;
- }
- gGL.end();
- }
- gGL.popUIMatrix();
-}
-
-// Renders a ring with sides (tube shape)
-void gl_deep_circle( F32 radius, F32 depth, S32 steps )
-{
- F32 x = radius;
- F32 y = 0.f;
- F32 angle_delta = F_TWO_PI / (F32)steps;
- gGL.begin( LLRender::TRIANGLE_STRIP );
- {
- S32 step = steps + 1; // An extra step to close the circle.
- while( step-- )
- {
- gGL.vertex3f( x, y, depth );
- gGL.vertex3f( x, y, 0.f );
-
- F32 x_new = x * cosf(angle_delta) - y * sinf(angle_delta);
- y = x * sinf(angle_delta) + y * cosf(angle_delta);
- x = x_new;
- }
- }
- gGL.end();
-}
-
-void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color, S32 steps, bool render_center )
-{
- gGL.pushUIMatrix();
- {
- gGL.translateUI(0.f, 0.f, -width / 2);
- if( render_center )
- {
- gGL.color4fv(center_color.mV);
- gGL.diffuseColor4fv(center_color.mV);
- gl_deep_circle( radius, width, steps );
- }
- else
- {
- gGL.diffuseColor4fv(side_color.mV);
- gl_washer_2d(radius, radius - width, steps, side_color, side_color);
- gGL.translateUI(0.f, 0.f, width);
- gl_washer_2d(radius - width, radius, steps, side_color, side_color);
- }
- }
- gGL.popUIMatrix();
-}
-
-// Draw gray and white checkerboard with black border
-void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha)
-{
- //polygon stipple is deprecated, use "Checker" texture
- LLPointer<LLUIImage> img = LLRender2D::getInstance()->getUIImage("Checker");
- gGL.getTexUnit(0)->bind(img->getImage());
- gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_WRAP);
- gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
-
- LLColor4 color(1.f, 1.f, 1.f, alpha);
- LLRectf uv_rect(0, 0, rect.getWidth()/32.f, rect.getHeight()/32.f);
-
- gl_draw_scaled_image(rect.mLeft, rect.mBottom, rect.getWidth(), rect.getHeight(), img->getImage(), color, uv_rect);
-
- gGL.flush();
-}
-
-
-// Draws the area between two concentric circles, like
-// a doughnut or washer.
-void gl_washer_2d(F32 outer_radius, F32 inner_radius, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color)
-{
- const F32 DELTA = F_TWO_PI / steps;
- const F32 SIN_DELTA = sin( DELTA );
- const F32 COS_DELTA = cos( DELTA );
-
- F32 x1 = outer_radius;
- F32 y1 = 0.f;
- F32 x2 = inner_radius;
- F32 y2 = 0.f;
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.begin( LLRender::TRIANGLE_STRIP );
- {
- steps += 1; // An extra step to close the circle.
- while( steps-- )
- {
- gGL.color4fv(outer_color.mV);
- gGL.vertex2f( x1, y1 );
- gGL.color4fv(inner_color.mV);
- gGL.vertex2f( x2, y2 );
-
- F32 x1_new = x1 * COS_DELTA - y1 * SIN_DELTA;
- y1 = x1 * SIN_DELTA + y1 * COS_DELTA;
- x1 = x1_new;
-
- F32 x2_new = x2 * COS_DELTA - y2 * SIN_DELTA;
- y2 = x2 * SIN_DELTA + y2 * COS_DELTA;
- x2 = x2_new;
- }
- }
- gGL.end();
-}
-
-// Draws the area between two concentric circles, like
-// a doughnut or washer.
-void gl_washer_segment_2d(F32 outer_radius, F32 inner_radius, F32 start_radians, F32 end_radians, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color)
-{
- const F32 DELTA = (end_radians - start_radians) / steps;
- const F32 SIN_DELTA = sin( DELTA );
- const F32 COS_DELTA = cos( DELTA );
-
- F32 x1 = outer_radius * cos( start_radians );
- F32 y1 = outer_radius * sin( start_radians );
- F32 x2 = inner_radius * cos( start_radians );
- F32 y2 = inner_radius * sin( start_radians );
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.begin( LLRender::TRIANGLE_STRIP );
- {
- steps += 1; // An extra step to close the circle.
- while( steps-- )
- {
- gGL.color4fv(outer_color.mV);
- gGL.vertex2f( x1, y1 );
- gGL.color4fv(inner_color.mV);
- gGL.vertex2f( x2, y2 );
-
- F32 x1_new = x1 * COS_DELTA - y1 * SIN_DELTA;
- y1 = x1 * SIN_DELTA + y1 * COS_DELTA;
- x1 = x1_new;
-
- F32 x2_new = x2 * COS_DELTA - y2 * SIN_DELTA;
- y2 = x2 * SIN_DELTA + y2 * COS_DELTA;
- x2 = x2_new;
- }
- }
- gGL.end();
-}
-
-void gl_rect_2d_simple_tex( S32 width, S32 height )
-{
- gGL.begin( LLRender::QUADS );
-
- gGL.texCoord2f(1.f, 1.f);
- gGL.vertex2i(width, height);
-
- gGL.texCoord2f(0.f, 1.f);
- gGL.vertex2i(0, height);
-
- gGL.texCoord2f(0.f, 0.f);
- gGL.vertex2i(0, 0);
-
- gGL.texCoord2f(1.f, 0.f);
- gGL.vertex2i(width, 0);
-
- gGL.end();
-}
-
-void gl_rect_2d_simple( S32 width, S32 height )
-{
- gGL.begin( LLRender::QUADS );
- gGL.vertex2i(width, height);
- gGL.vertex2i(0, height);
- gGL.vertex2i(0, 0);
- gGL.vertex2i(width, 0);
- gGL.end();
-}
-
-void gl_segmented_rect_2d_tex(const S32 left,
- const S32 top,
- const S32 right,
- const S32 bottom,
- const S32 texture_width,
- const S32 texture_height,
- const S32 border_size,
- const U32 edges)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
-
- S32 width = llabs(right - left);
- S32 height = llabs(top - bottom);
-
- gGL.pushUIMatrix();
-
- gGL.translateUI((F32)left, (F32)bottom, 0.f);
- LLVector2 border_uv_scale((F32)border_size / (F32)texture_width, (F32)border_size / (F32)texture_height);
-
- if (border_uv_scale.mV[VX] > 0.5f)
- {
- border_uv_scale *= 0.5f / border_uv_scale.mV[VX];
- }
- if (border_uv_scale.mV[VY] > 0.5f)
- {
- border_uv_scale *= 0.5f / border_uv_scale.mV[VY];
- }
-
- F32 border_scale = llmin((F32)border_size, (F32)width * 0.5f, (F32)height * 0.5f);
- LLVector2 border_width_left = ((edges & (~(U32)ROUNDED_RECT_RIGHT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
- LLVector2 border_width_right = ((edges & (~(U32)ROUNDED_RECT_LEFT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
- LLVector2 border_height_bottom = ((edges & (~(U32)ROUNDED_RECT_TOP)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
- LLVector2 border_height_top = ((edges & (~(U32)ROUNDED_RECT_BOTTOM)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
- LLVector2 width_vec((F32)width, 0.f);
- LLVector2 height_vec(0.f, (F32)height);
-
- gGL.begin(LLRender::QUADS);
- {
- // draw bottom left
- gGL.texCoord2f(0.f, 0.f);
- gGL.vertex2f(0.f, 0.f);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv(border_width_left.mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + border_height_bottom).mV);
-
- gGL.texCoord2f(0.f, border_uv_scale.mV[VY]);
- gGL.vertex2fv(border_height_bottom.mV);
-
- // draw bottom middle
- gGL.texCoord2f(border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv(border_width_left.mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv((width_vec - border_width_right).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + border_height_bottom).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + border_height_bottom).mV);
-
- // draw bottom right
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv((width_vec - border_width_right).mV);
-
- gGL.texCoord2f(1.f, 0.f);
- gGL.vertex2fv(width_vec.mV);
-
- gGL.texCoord2f(1.f, border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + border_height_bottom).mV);
-
- // draw left
- gGL.texCoord2f(0.f, border_uv_scale.mV[VY]);
- gGL.vertex2fv(border_height_bottom.mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + border_height_bottom).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(0.f, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((height_vec - border_height_top).mV);
-
- // draw middle
- gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + height_vec - border_height_top).mV);
-
- // draw right
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f, border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
-
- // draw top left
- gGL.texCoord2f(0.f, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((height_vec - border_height_top).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f);
- gGL.vertex2fv((border_width_left + height_vec).mV);
-
- gGL.texCoord2f(0.f, 1.f);
- gGL.vertex2fv((height_vec).mV);
-
- // draw top middle
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f);
- gGL.vertex2fv((width_vec - border_width_right + height_vec).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f);
- gGL.vertex2fv((border_width_left + height_vec).mV);
-
- // draw top right
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f, 1.f);
- gGL.vertex2fv((width_vec + height_vec).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f);
- gGL.vertex2fv((width_vec - border_width_right + height_vec).mV);
- }
- gGL.end();
-
- gGL.popUIMatrix();
-}
-
-void gl_segmented_rect_2d_fragment_tex(const LLRect& rect,
- const S32 texture_width,
- const S32 texture_height,
- const S32 border_size,
- const F32 start_fragment,
- const F32 end_fragment,
- const U32 edges)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- const S32 left = rect.mLeft;
- const S32 right = rect.mRight;
- const S32 top = rect.mTop;
- const S32 bottom = rect.mBottom;
- S32 width = llabs(right - left);
- S32 height = llabs(top - bottom);
-
- gGL.pushUIMatrix();
-
- gGL.translateUI((F32)left, (F32)bottom, 0.f);
- LLVector2 border_uv_scale((F32)border_size / (F32)texture_width, (F32)border_size / (F32)texture_height);
-
- if (border_uv_scale.mV[VX] > 0.5f)
- {
- border_uv_scale *= 0.5f / border_uv_scale.mV[VX];
- }
- if (border_uv_scale.mV[VY] > 0.5f)
- {
- border_uv_scale *= 0.5f / border_uv_scale.mV[VY];
- }
-
- F32 border_scale = llmin((F32)border_size, (F32)width * 0.5f, (F32)height * 0.5f);
- LLVector2 border_width_left = ((edges & (~(U32)ROUNDED_RECT_RIGHT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
- LLVector2 border_width_right = ((edges & (~(U32)ROUNDED_RECT_LEFT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
- LLVector2 border_height_bottom = ((edges & (~(U32)ROUNDED_RECT_TOP)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
- LLVector2 border_height_top = ((edges & (~(U32)ROUNDED_RECT_BOTTOM)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
- LLVector2 width_vec((F32)width, 0.f);
- LLVector2 height_vec(0.f, (F32)height);
-
- F32 middle_start = border_scale / (F32)width;
- F32 middle_end = 1.f - middle_start;
-
- F32 u_min;
- F32 u_max;
- LLVector2 x_min;
- LLVector2 x_max;
-
- gGL.begin(LLRender::QUADS);
- {
- if (start_fragment < middle_start)
- {
- u_min = (start_fragment / middle_start) * border_uv_scale.mV[VX];
- u_max = llmin(end_fragment / middle_start, 1.f) * border_uv_scale.mV[VX];
- x_min = (start_fragment / middle_start) * border_width_left;
- x_max = llmin(end_fragment / middle_start, 1.f) * border_width_left;
-
- // draw bottom left
- gGL.texCoord2f(u_min, 0.f);
- gGL.vertex2fv(x_min.mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv(x_max.mV);
-
- gGL.texCoord2f(u_max, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + border_height_bottom).mV);
-
- gGL.texCoord2f(u_min, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + border_height_bottom).mV);
-
- // draw left
- gGL.texCoord2f(u_min, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + border_height_bottom).mV);
-
- gGL.texCoord2f(u_max, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + border_height_bottom).mV);
-
- gGL.texCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
-
- // draw top left
- gGL.texCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(u_max, 1.f);
- gGL.vertex2fv((x_max + height_vec).mV);
-
- gGL.texCoord2f(u_min, 1.f);
- gGL.vertex2fv((x_min + height_vec).mV);
- }
-
- if (end_fragment > middle_start || start_fragment < middle_end)
- {
- x_min = border_width_left + ((llclamp(start_fragment, middle_start, middle_end) - middle_start)) * width_vec;
- x_max = border_width_left + ((llclamp(end_fragment, middle_start, middle_end) - middle_start)) * width_vec;
-
- // draw bottom middle
- gGL.texCoord2f(border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv(x_min.mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv((x_max).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + border_height_bottom).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + border_height_bottom).mV);
-
- // draw middle
- gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
-
- // draw top middle
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f);
- gGL.vertex2fv((x_max + height_vec).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f);
- gGL.vertex2fv((x_min + height_vec).mV);
- }
-
- if (end_fragment > middle_end)
- {
- u_min = 1.f - ((1.f - llmax(0.f, (start_fragment - middle_end) / middle_start)) * border_uv_scale.mV[VX]);
- u_max = 1.f - ((1.f - ((end_fragment - middle_end) / middle_start)) * border_uv_scale.mV[VX]);
- x_min = width_vec - ((1.f - llmax(0.f, (start_fragment - middle_end) / middle_start)) * border_width_right);
- x_max = width_vec - ((1.f - ((end_fragment - middle_end) / middle_start)) * border_width_right);
-
- // draw bottom right
- gGL.texCoord2f(u_min, 0.f);
- gGL.vertex2fv((x_min).mV);
-
- gGL.texCoord2f(u_max, 0.f);
- gGL.vertex2fv(x_max.mV);
-
- gGL.texCoord2f(u_max, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + border_height_bottom).mV);
-
- gGL.texCoord2f(u_min, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + border_height_bottom).mV);
-
- // draw right
- gGL.texCoord2f(u_min, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + border_height_bottom).mV);
-
- gGL.texCoord2f(u_max, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + border_height_bottom).mV);
-
- gGL.texCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
-
- // draw top right
- gGL.texCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(u_max, 1.f);
- gGL.vertex2fv((x_max + height_vec).mV);
-
- gGL.texCoord2f(u_min, 1.f);
- gGL.vertex2fv((x_min + height_vec).mV);
- }
- }
- gGL.end();
-
- gGL.popUIMatrix();
-}
-
-void gl_segmented_rect_3d_tex(const LLRectf& clip_rect, const LLRectf& center_uv_rect, const LLRectf& center_draw_rect,
- const LLVector3& width_vec, const LLVector3& height_vec)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
-
- gGL.begin(LLRender::QUADS);
- {
- // draw bottom left
- gGL.texCoord2f(clip_rect.mLeft, clip_rect.mBottom);
- gGL.vertex3f(0.f, 0.f, 0.f);
-
- gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mLeft * width_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV);
-
- gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mBottom * height_vec).mV);
-
- // draw bottom middle
- gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mLeft * width_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mRight * width_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV);
-
- // draw bottom right
- gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mRight * width_vec).mV);
-
- gGL.texCoord2f(clip_rect.mRight, clip_rect.mBottom);
- gGL.vertex3fv(width_vec.mV);
-
- gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mBottom);
- gGL.vertex3fv((width_vec + center_draw_rect.mBottom * height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV);
-
- // draw left
- gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mBottom * height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV);
-
- gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mTop * height_vec).mV);
-
- // draw middle
- gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV);
-
- // draw right
- gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom);
- gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV);
-
- gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mBottom);
- gGL.vertex3fv((width_vec + center_draw_rect.mBottom * height_vec).mV);
-
- gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mTop);
- gGL.vertex3fv((width_vec + center_draw_rect.mTop * height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV);
-
- // draw top left
- gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mTop * height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV);
-
- gGL.texCoord2f(clip_rect.mLeft, clip_rect.mTop);
- gGL.vertex3fv((height_vec).mV);
-
- // draw top middle
- gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mRight * width_vec + height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV);
-
- // draw top right
- gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV);
-
- gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mTop);
- gGL.vertex3fv((width_vec + center_draw_rect.mTop * height_vec).mV);
-
- gGL.texCoord2f(clip_rect.mRight, clip_rect.mTop);
- gGL.vertex3fv((width_vec + height_vec).mV);
-
- gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mTop);
- gGL.vertex3fv((center_draw_rect.mRight * width_vec + height_vec).mV);
- }
- gGL.end();
-
-}
-
-LLRender2D::LLRender2D(LLImageProviderInterface* image_provider)
-{
- mImageProvider = image_provider;
- if(mImageProvider)
- {
- mImageProvider->addOnRemovalCallback(resetProvider);
- }
-}
-
-LLRender2D::~LLRender2D()
-{
- if(mImageProvider)
- {
- mImageProvider->cleanUp();
- mImageProvider->deleteOnRemovalCallback(resetProvider);
- }
-}
-
-// static
-void LLRender2D::translate(F32 x, F32 y, F32 z)
-{
- gGL.translateUI(x,y,z);
- LLFontGL::sCurOrigin.mX += (S32) x;
- LLFontGL::sCurOrigin.mY += (S32) y;
- LLFontGL::sCurDepth += z;
-}
-
-// static
-void LLRender2D::pushMatrix()
-{
- gGL.pushUIMatrix();
- LLFontGL::sOriginStack.push_back(std::make_pair(LLFontGL::sCurOrigin, LLFontGL::sCurDepth));
-}
-
-// static
-void LLRender2D::popMatrix()
-{
- gGL.popUIMatrix();
- LLFontGL::sCurOrigin = LLFontGL::sOriginStack.back().first;
- LLFontGL::sCurDepth = LLFontGL::sOriginStack.back().second;
- LLFontGL::sOriginStack.pop_back();
-}
-
-// static
-void LLRender2D::loadIdentity()
-{
- gGL.loadUIIdentity();
- LLFontGL::sCurOrigin.mX = 0;
- LLFontGL::sCurOrigin.mY = 0;
- LLFontGL::sCurDepth = 0.f;
-}
-
-// static
-void LLRender2D::setLineWidth(F32 width)
-{
- gGL.flush();
- // If outside the allowed range, glLineWidth fails with "invalid value".
- // On Darwin, the range is [1, 1].
- static GLfloat range[2]{0.0};
- if (range[1] == 0)
- {
- glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, range);
- }
- width *= lerp(LLRender::sUIGLScaleFactor.mV[VX], LLRender::sUIGLScaleFactor.mV[VY], 0.5f);
- glLineWidth(llclamp(width, range[0], range[1]));
-}
-
-LLPointer<LLUIImage> LLRender2D::getUIImageByID(const LLUUID& image_id, S32 priority)
-{
- if (mImageProvider)
- {
- return mImageProvider->getUIImageByID(image_id, priority);
- }
- else
- {
- return NULL;
- }
-}
-
-LLPointer<LLUIImage> LLRender2D::getUIImage(const std::string& name, S32 priority)
-{
- if (!name.empty() && mImageProvider)
- return mImageProvider->getUIImage(name, priority);
- else
- return NULL;
-}
-
-// static
-void LLRender2D::resetProvider()
-{
- if (LLRender2D::instanceExists())
- {
- LLRender2D::getInstance()->mImageProvider = NULL;
- }
-}
-
-// class LLImageProviderInterface
-
-LLImageProviderInterface::~LLImageProviderInterface()
-{
- for (callback_list_t::iterator iter = mCallbackList.begin(); iter != mCallbackList.end();)
- {
- callback_list_t::iterator curiter = iter++;
- (*curiter)();
- }
-}
-
-void LLImageProviderInterface::addOnRemovalCallback(callback_t func)
-{
- if (!func)
- {
- return;
- }
- mCallbackList.push_back(func);
-}
-
-void LLImageProviderInterface::deleteOnRemovalCallback(callback_t func)
-{
- callback_list_t::iterator iter = std::find(mCallbackList.begin(), mCallbackList.end(), func);
- if (iter != mCallbackList.end())
- {
- mCallbackList.erase(iter);
- }
-}
-
+/**
+ * @file llrender2dutils.cpp
+ * @brief GL function implementations for immediate-mode gl drawing.
+ *
+ * $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"
+
+// Linden library includes
+#include "v2math.h"
+#include "m3math.h"
+#include "v4color.h"
+#include "llfontgl.h"
+#include "llrender.h"
+#include "llrect.h"
+#include "llgl.h"
+#include "lltexture.h"
+#include "llfasttimer.h"
+
+// Project includes
+#include "llrender2dutils.h"
+#include "lluiimage.h"
+
+
+//
+// Globals
+//
+const LLColor4 UI_VERTEX_COLOR(1.f, 1.f, 1.f, 1.f);
+
+//
+// Functions
+//
+
+bool ui_point_in_rect(S32 x, S32 y, S32 left, S32 top, S32 right, S32 bottom)
+{
+ if (x < left || right < x) return false;
+ if (y < bottom || top < y) return false;
+ return true;
+}
+
+
+// Puts GL into 2D drawing mode by turning off lighting, setting to an
+// orthographic projection, etc.
+void gl_state_for_2d(S32 width, S32 height)
+{
+ stop_glerror();
+ F32 window_width = (F32) width;//gViewerWindow->getWindowWidth();
+ F32 window_height = (F32) height;//gViewerWindow->getWindowHeight();
+
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.loadIdentity();
+ gGL.ortho(0.0f, llmax(window_width, 1.f), 0.0f, llmax(window_height,1.f), -1.0f, 1.0f);
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ gGL.loadIdentity();
+ stop_glerror();
+}
+
+
+void gl_draw_x(const LLRect& rect, const LLColor4& color)
+{
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ gGL.color4fv( color.mV );
+
+ gGL.begin( LLRender::LINES );
+ gGL.vertex2i( rect.mLeft, rect.mTop );
+ gGL.vertex2i( rect.mRight, rect.mBottom );
+ gGL.vertex2i( rect.mLeft, rect.mBottom );
+ gGL.vertex2i( rect.mRight, rect.mTop );
+ gGL.end();
+}
+
+
+void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, S32 pixel_offset, bool filled)
+{
+ gGL.color4fv(color.mV);
+ gl_rect_2d_offset_local(left, top, right, bottom, pixel_offset, filled);
+}
+
+void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixel_offset, bool filled)
+{
+ gGL.pushUIMatrix();
+ left += LLFontGL::sCurOrigin.mX;
+ right += LLFontGL::sCurOrigin.mX;
+ bottom += LLFontGL::sCurOrigin.mY;
+ top += LLFontGL::sCurOrigin.mY;
+
+ gGL.loadUIIdentity();
+ gl_rect_2d(llfloor((F32)left * LLRender::sUIGLScaleFactor.mV[VX]) - pixel_offset,
+ llfloor((F32)top * LLRender::sUIGLScaleFactor.mV[VY]) + pixel_offset,
+ llfloor((F32)right * LLRender::sUIGLScaleFactor.mV[VX]) + pixel_offset,
+ llfloor((F32)bottom * LLRender::sUIGLScaleFactor.mV[VY]) - pixel_offset,
+ filled);
+ gGL.popUIMatrix();
+}
+
+
+void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, bool filled )
+{
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ // Counterclockwise quad will face the viewer
+ if( filled )
+ {
+ gGL.begin( LLRender::QUADS );
+ gGL.vertex2i(left, top);
+ gGL.vertex2i(left, bottom);
+ gGL.vertex2i(right, bottom);
+ gGL.vertex2i(right, top);
+ gGL.end();
+ }
+ else
+ {
+ top--;
+ right--;
+ gGL.begin( LLRender::LINE_STRIP );
+ gGL.vertex2i(left, top);
+ gGL.vertex2i(left, bottom);
+ gGL.vertex2i(right, bottom);
+ gGL.vertex2i(right, top);
+ gGL.vertex2i(left, top);
+ gGL.end();
+ }
+}
+
+void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, bool filled )
+{
+ gGL.color4fv( color.mV );
+ gl_rect_2d( left, top, right, bottom, filled );
+}
+
+
+void gl_rect_2d( const LLRect& rect, const LLColor4& color, bool filled )
+{
+ gGL.color4fv( color.mV );
+ gl_rect_2d( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, filled );
+}
+
+// Given a rectangle on the screen, draws a drop shadow _outside_
+// the right and bottom edges of it. Along the right it has width "lines"
+// and along the bottom it has height "lines".
+void gl_drop_shadow(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &start_color, S32 lines)
+{
+ stop_glerror();
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ // HACK: Overlap with the rectangle by a single pixel.
+ right--;
+ bottom++;
+ lines++;
+
+ LLColor4 end_color = start_color;
+ end_color.mV[VALPHA] = 0.f;
+
+ gGL.begin(LLRender::QUADS);
+
+ // Right edge, CCW faces screen
+ gGL.color4fv(start_color.mV);
+ gGL.vertex2i(right, top-lines);
+ gGL.vertex2i(right, bottom);
+ gGL.color4fv(end_color.mV);
+ gGL.vertex2i(right+lines, bottom);
+ gGL.vertex2i(right+lines, top-lines);
+
+ // Bottom edge, CCW faces screen
+ gGL.color4fv(start_color.mV);
+ gGL.vertex2i(right, bottom);
+ gGL.vertex2i(left+lines, bottom);
+ gGL.color4fv(end_color.mV);
+ gGL.vertex2i(left+lines, bottom-lines);
+ gGL.vertex2i(right, bottom-lines);
+
+ // bottom left Corner
+ gGL.color4fv(start_color.mV);
+ gGL.vertex2i(left+lines, bottom);
+ gGL.color4fv(end_color.mV);
+ gGL.vertex2i(left, bottom);
+ // make the bottom left corner not sharp
+ gGL.vertex2i(left+1, bottom-lines+1);
+ gGL.vertex2i(left+lines, bottom-lines);
+
+ // bottom right corner
+ gGL.color4fv(start_color.mV);
+ gGL.vertex2i(right, bottom);
+ gGL.color4fv(end_color.mV);
+ gGL.vertex2i(right, bottom-lines);
+ // make the rightmost corner not sharp
+ gGL.vertex2i(right+lines-1, bottom-lines+1);
+ gGL.vertex2i(right+lines, bottom);
+
+ // top right corner
+ gGL.color4fv(start_color.mV);
+ gGL.vertex2i( right, top-lines );
+ gGL.color4fv(end_color.mV);
+ gGL.vertex2i( right+lines, top-lines );
+ // make the corner not sharp
+ gGL.vertex2i( right+lines-1, top-1 );
+ gGL.vertex2i( right, top );
+
+ gGL.end();
+ stop_glerror();
+}
+
+void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2 )
+{
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ gGL.begin(LLRender::LINES);
+ gGL.vertex2i(x1, y1);
+ gGL.vertex2i(x2, y2);
+ gGL.end();
+}
+
+void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2, const LLColor4 &color )
+{
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ gGL.color4fv( color.mV );
+
+ gGL.begin(LLRender::LINES);
+ gGL.vertex2i(x1, y1);
+ gGL.vertex2i(x2, y2);
+ gGL.end();
+}
+
+void gl_triangle_2d(S32 x1, S32 y1, S32 x2, S32 y2, S32 x3, S32 y3, const LLColor4& color, bool filled)
+{
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ gGL.color4fv(color.mV);
+
+ if (filled)
+ {
+ gGL.begin(LLRender::TRIANGLES);
+ }
+ else
+ {
+ gGL.begin(LLRender::LINE_LOOP);
+ }
+ gGL.vertex2i(x1, y1);
+ gGL.vertex2i(x2, y2);
+ gGL.vertex2i(x3, y3);
+ gGL.end();
+}
+
+void gl_corners_2d(S32 left, S32 top, S32 right, S32 bottom, S32 length, F32 max_frac)
+{
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ length = llmin((S32)(max_frac*(right - left)), length);
+ length = llmin((S32)(max_frac*(top - bottom)), length);
+ gGL.begin(LLRender::LINES);
+ gGL.vertex2i(left, top);
+ gGL.vertex2i(left + length, top);
+
+ gGL.vertex2i(left, top);
+ gGL.vertex2i(left, top - length);
+
+ gGL.vertex2i(left, bottom);
+ gGL.vertex2i(left + length, bottom);
+
+ gGL.vertex2i(left, bottom);
+ gGL.vertex2i(left, bottom + length);
+
+ gGL.vertex2i(right, top);
+ gGL.vertex2i(right - length, top);
+
+ gGL.vertex2i(right, top);
+ gGL.vertex2i(right, top - length);
+
+ gGL.vertex2i(right, bottom);
+ gGL.vertex2i(right - length, bottom);
+
+ gGL.vertex2i(right, bottom);
+ gGL.vertex2i(right, bottom + length);
+ gGL.end();
+}
+
+
+void gl_draw_image( S32 x, S32 y, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect )
+{
+ if (NULL == image)
+ {
+ LL_WARNS() << "image == NULL; aborting function" << LL_ENDL;
+ return;
+ }
+ gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), 0.f, image, color, uv_rect );
+}
+
+void gl_draw_scaled_target(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* target, const LLColor4& color, const LLRectf& uv_rect)
+{
+ gl_draw_scaled_rotated_image(x, y, width, height, 0.f, NULL, color, uv_rect, target);
+}
+
+void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect)
+{
+ if (NULL == image)
+ {
+ LL_WARNS() << "image == NULL; aborting function" << LL_ENDL;
+ return;
+ }
+ gl_draw_scaled_rotated_image( x, y, width, height, 0.f, image, color, uv_rect );
+}
+
+void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLTexture* image, const LLColor4& color, bool solid_color, const LLRectf& uv_rect, bool scale_inner)
+{
+ if (NULL == image)
+ {
+ LL_WARNS() << "image == NULL; aborting function" << LL_ENDL;
+ return;
+ }
+
+ // scale screen size of borders down
+ F32 border_width_fraction = (F32)border_width / (F32)image->getWidth(0);
+ F32 border_height_fraction = (F32)border_height / (F32)image->getHeight(0);
+
+ LLRectf scale_rect(border_width_fraction, 1.f - border_height_fraction, 1.f - border_width_fraction, border_height_fraction);
+ gl_draw_scaled_image_with_border(x, y, width, height, image, color, solid_color, uv_rect, scale_rect, scale_inner);
+}
+
+void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTexture* image, const LLColor4& color, bool solid_color, const LLRectf& uv_outer_rect, const LLRectf& center_rect, bool scale_inner)
+{
+ stop_glerror();
+
+ if (NULL == image)
+ {
+ LL_WARNS() << "image == NULL; aborting function" << LL_ENDL;
+ return;
+ }
+
+ if (solid_color)
+ {
+ gSolidColorProgram.bind();
+ }
+
+ if (center_rect.mLeft == 0.f
+ && center_rect.mRight == 1.f
+ && center_rect.mBottom == 0.f
+ && center_rect.mTop == 1.f)
+ {
+ gl_draw_scaled_image(x, y, width, height, image, color, uv_outer_rect);
+ }
+ else
+ {
+ // add in offset of current image to current UI translation
+ const LLVector3 ui_scale = gGL.getUIScale();
+ const LLVector3 ui_translation = (gGL.getUITranslation() + LLVector3(x, y, 0.f)).scaledVec(ui_scale);
+
+ F32 uv_width = uv_outer_rect.getWidth();
+ F32 uv_height = uv_outer_rect.getHeight();
+
+ // shrink scaling region to be proportional to clipped image region
+ LLRectf uv_center_rect( uv_outer_rect.mLeft + (center_rect.mLeft * uv_width),
+ uv_outer_rect.mBottom + (center_rect.mTop * uv_height),
+ uv_outer_rect.mLeft + (center_rect.mRight * uv_width),
+ uv_outer_rect.mBottom + (center_rect.mBottom * uv_height));
+
+ F32 image_width = image->getWidth(0);
+ F32 image_height = image->getHeight(0);
+
+ S32 image_natural_width = ll_round(image_width * uv_width);
+ S32 image_natural_height = ll_round(image_height * uv_height);
+
+ LLRectf draw_center_rect( uv_center_rect.mLeft * image_width,
+ uv_center_rect.mTop * image_height,
+ uv_center_rect.mRight * image_width,
+ uv_center_rect.mBottom * image_height);
+
+ if (scale_inner)
+ {
+ // scale center region of image to drawn region
+ draw_center_rect.mRight += width - image_natural_width;
+ draw_center_rect.mTop += height - image_natural_height;
+
+ const F32 border_shrink_width = llmax(0.f, draw_center_rect.mLeft - draw_center_rect.mRight);
+ const F32 border_shrink_height = llmax(0.f, draw_center_rect.mBottom - draw_center_rect.mTop);
+
+ const F32 shrink_width_ratio = center_rect.getWidth() == 1.f ? 0.f : border_shrink_width / ((F32)image_natural_width * (1.f - center_rect.getWidth()));
+ const F32 shrink_height_ratio = center_rect.getHeight() == 1.f ? 0.f : border_shrink_height / ((F32)image_natural_height * (1.f - center_rect.getHeight()));
+
+ const F32 border_shrink_scale = 1.f - llmax(shrink_width_ratio, shrink_height_ratio);
+ draw_center_rect.mLeft *= border_shrink_scale;
+ draw_center_rect.mTop = lerp((F32)height, (F32)draw_center_rect.mTop, border_shrink_scale);
+ draw_center_rect.mRight = lerp((F32)width, (F32)draw_center_rect.mRight, border_shrink_scale);
+ draw_center_rect.mBottom *= border_shrink_scale;
+ }
+ else
+ {
+ // keep center region of image at fixed scale, but in same relative position
+ F32 scale_factor = llmin((F32)width / draw_center_rect.getWidth(), (F32)height / draw_center_rect.getHeight(), 1.f);
+ F32 scaled_width = draw_center_rect.getWidth() * scale_factor;
+ F32 scaled_height = draw_center_rect.getHeight() * scale_factor;
+ draw_center_rect.setCenterAndSize(uv_center_rect.getCenterX() * width, uv_center_rect.getCenterY() * height, scaled_width, scaled_height);
+ }
+
+ draw_center_rect.mLeft = ll_round(ui_translation.mV[VX] + (F32)draw_center_rect.mLeft * ui_scale.mV[VX]);
+ draw_center_rect.mTop = ll_round(ui_translation.mV[VY] + (F32)draw_center_rect.mTop * ui_scale.mV[VY]);
+ draw_center_rect.mRight = ll_round(ui_translation.mV[VX] + (F32)draw_center_rect.mRight * ui_scale.mV[VX]);
+ draw_center_rect.mBottom = ll_round(ui_translation.mV[VY] + (F32)draw_center_rect.mBottom * ui_scale.mV[VY]);
+
+ LLRectf draw_outer_rect(ui_translation.mV[VX],
+ ui_translation.mV[VY] + height * ui_scale.mV[VY],
+ ui_translation.mV[VX] + width * ui_scale.mV[VX],
+ ui_translation.mV[VY]);
+
+ LLGLSUIDefault gls_ui;
+
+ gGL.getTexUnit(0)->bind(image, true);
+
+ gGL.color4fv(color.mV);
+
+ const S32 NUM_VERTICES = 9 * 4; // 9 quads
+ LLVector2 uv[NUM_VERTICES];
+ LLVector3 pos[NUM_VERTICES];
+
+ S32 index = 0;
+
+ gGL.begin(LLRender::QUADS);
+ {
+ // draw bottom left
+ uv[index] = LLVector2(uv_outer_rect.mLeft, uv_outer_rect.mBottom);
+ pos[index] = LLVector3(draw_outer_rect.mLeft, draw_outer_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_outer_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_outer_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_outer_rect.mLeft, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_outer_rect.mLeft, draw_center_rect.mBottom, 0.f);
+ index++;
+
+ // draw bottom middle
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_outer_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_outer_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_outer_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_outer_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mBottom, 0.f);
+ index++;
+
+ // draw bottom right
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_outer_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_outer_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_outer_rect.mRight, uv_outer_rect.mBottom);
+ pos[index] = LLVector3(draw_outer_rect.mRight, draw_outer_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_outer_rect.mRight, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_outer_rect.mRight, draw_center_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mBottom, 0.f);
+ index++;
+
+ // draw left
+ uv[index] = LLVector2(uv_outer_rect.mLeft, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_outer_rect.mLeft, draw_center_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mTop, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_outer_rect.mLeft, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_outer_rect.mLeft, draw_center_rect.mTop, 0.f);
+ index++;
+
+ // draw middle
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mTop, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mTop, 0.f);
+ index++;
+
+ // draw right
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_outer_rect.mRight, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_outer_rect.mRight, draw_center_rect.mBottom, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_outer_rect.mRight, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_outer_rect.mRight, draw_center_rect.mTop, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mTop, 0.f);
+ index++;
+
+ // draw top left
+ uv[index] = LLVector2(uv_outer_rect.mLeft, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_outer_rect.mLeft, draw_center_rect.mTop, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mTop, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_outer_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_outer_rect.mTop, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_outer_rect.mLeft, uv_outer_rect.mTop);
+ pos[index] = LLVector3(draw_outer_rect.mLeft, draw_outer_rect.mTop, 0.f);
+ index++;
+
+ // draw top middle
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mTop, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mTop, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_outer_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_outer_rect.mTop, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_outer_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_outer_rect.mTop, 0.f);
+ index++;
+
+ // draw top right
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mTop, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_outer_rect.mRight, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_outer_rect.mRight, draw_center_rect.mTop, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_outer_rect.mRight, uv_outer_rect.mTop);
+ pos[index] = LLVector3(draw_outer_rect.mRight, draw_outer_rect.mTop, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_outer_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_outer_rect.mTop, 0.f);
+ index++;
+
+ gGL.vertexBatchPreTransformed(pos, uv, NUM_VERTICES);
+ }
+ gGL.end();
+ }
+
+ if (solid_color)
+ {
+ gUIProgram.bind();
+ }
+}
+
+void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect)
+{
+ gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), degrees, image, color, uv_rect );
+}
+
+void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect, LLRenderTarget* target)
+{
+ if (!image && !target)
+ {
+ LL_WARNS() << "image == NULL; aborting function" << LL_ENDL;
+ return;
+ }
+
+ LLGLSUIDefault gls_ui;
+
+ if(image != NULL)
+ {
+ gGL.getTexUnit(0)->bind(image, true);
+ }
+ else
+ {
+ gGL.getTexUnit(0)->bind(target);
+ }
+
+ gGL.color4fv(color.mV);
+
+ if (degrees == 0.f)
+ {
+ const S32 NUM_VERTICES = 4; // 9 quads
+ LLVector2 uv[NUM_VERTICES];
+ LLVector3 pos[NUM_VERTICES];
+
+ gGL.begin(LLRender::QUADS);
+ {
+ LLVector3 ui_scale = gGL.getUIScale();
+ LLVector3 ui_translation = gGL.getUITranslation();
+ ui_translation.mV[VX] += x;
+ ui_translation.mV[VY] += y;
+ ui_translation.scaleVec(ui_scale);
+ S32 index = 0;
+ S32 scaled_width = ll_round(width * ui_scale.mV[VX]);
+ S32 scaled_height = ll_round(height * ui_scale.mV[VY]);
+
+ uv[index] = LLVector2(uv_rect.mRight, uv_rect.mTop);
+ pos[index] = LLVector3(ui_translation.mV[VX] + scaled_width, ui_translation.mV[VY] + scaled_height, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_rect.mLeft, uv_rect.mTop);
+ pos[index] = LLVector3(ui_translation.mV[VX], ui_translation.mV[VY] + scaled_height, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_rect.mLeft, uv_rect.mBottom);
+ pos[index] = LLVector3(ui_translation.mV[VX], ui_translation.mV[VY], 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_rect.mRight, uv_rect.mBottom);
+ pos[index] = LLVector3(ui_translation.mV[VX] + scaled_width, ui_translation.mV[VY], 0.f);
+ index++;
+
+ gGL.vertexBatchPreTransformed(pos, uv, NUM_VERTICES);
+ }
+ gGL.end();
+ }
+ else
+ {
+ gGL.pushUIMatrix();
+ gGL.translateUI((F32)x, (F32)y, 0.f);
+
+ F32 offset_x = F32(width/2);
+ F32 offset_y = F32(height/2);
+
+ gGL.translateUI(offset_x, offset_y, 0.f);
+
+ LLMatrix3 quat(0.f, 0.f, degrees*DEG_TO_RAD);
+
+ if(image != NULL)
+ {
+ gGL.getTexUnit(0)->bind(image, true);
+ }
+ else
+ {
+ gGL.getTexUnit(0)->bind(target);
+ }
+
+ gGL.color4fv(color.mV);
+
+ gGL.begin(LLRender::QUADS);
+ {
+ LLVector3 v;
+
+ v = LLVector3(offset_x, offset_y, 0.f) * quat;
+ gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop);
+ gGL.vertex2f(v.mV[0], v.mV[1] );
+
+ v = LLVector3(-offset_x, offset_y, 0.f) * quat;
+ gGL.texCoord2f(uv_rect.mLeft, uv_rect.mTop);
+ gGL.vertex2f(v.mV[0], v.mV[1] );
+
+ v = LLVector3(-offset_x, -offset_y, 0.f) * quat;
+ gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom);
+ gGL.vertex2f(v.mV[0], v.mV[1] );
+
+ v = LLVector3(offset_x, -offset_y, 0.f) * quat;
+ gGL.texCoord2f(uv_rect.mRight, uv_rect.mBottom);
+ gGL.vertex2f(v.mV[0], v.mV[1] );
+ }
+ gGL.end();
+ gGL.popUIMatrix();
+ }
+}
+
+void gl_line_3d( const LLVector3& start, const LLVector3& end, const LLColor4& color)
+{
+ gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], color.mV[VALPHA]);
+
+ gGL.flush();
+ glLineWidth(2.5f);
+
+ gGL.begin(LLRender::LINES);
+ {
+ gGL.vertex3fv( start.mV );
+ gGL.vertex3fv( end.mV );
+ }
+ gGL.end();
+
+ LLRender2D::getInstance()->setLineWidth(1.f);
+}
+
+void gl_arc_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, bool filled, F32 start_angle, F32 end_angle)
+{
+ if (end_angle < start_angle)
+ {
+ end_angle += F_TWO_PI;
+ }
+
+ gGL.pushUIMatrix();
+ {
+ gGL.translateUI(center_x, center_y, 0.f);
+
+ // Inexact, but reasonably fast.
+ F32 delta = (end_angle - start_angle) / steps;
+ F32 sin_delta = sin( delta );
+ F32 cos_delta = cos( delta );
+ F32 x = cosf(start_angle) * radius;
+ F32 y = sinf(start_angle) * radius;
+
+ if (filled)
+ {
+ gGL.begin(LLRender::TRIANGLE_FAN);
+ gGL.vertex2f(0.f, 0.f);
+ // make sure circle is complete
+ steps += 1;
+ }
+ else
+ {
+ gGL.begin(LLRender::LINE_STRIP);
+ }
+
+ while( steps-- )
+ {
+ // Successive rotations
+ gGL.vertex2f( x, y );
+ F32 x_new = x * cos_delta - y * sin_delta;
+ y = x * sin_delta + y * cos_delta;
+ x = x_new;
+ }
+ gGL.end();
+ }
+ gGL.popUIMatrix();
+}
+
+void gl_circle_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, bool filled)
+{
+ gGL.pushUIMatrix();
+ {
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.translateUI(center_x, center_y, 0.f);
+
+ // Inexact, but reasonably fast.
+ F32 delta = F_TWO_PI / steps;
+ F32 sin_delta = sin( delta );
+ F32 cos_delta = cos( delta );
+ F32 x = radius;
+ F32 y = 0.f;
+
+ if (filled)
+ {
+ gGL.begin(LLRender::TRIANGLE_FAN);
+ gGL.vertex2f(0.f, 0.f);
+ // make sure circle is complete
+ steps += 1;
+ }
+ else
+ {
+ gGL.begin(LLRender::LINE_LOOP);
+ }
+
+ while( steps-- )
+ {
+ // Successive rotations
+ gGL.vertex2f( x, y );
+ F32 x_new = x * cos_delta - y * sin_delta;
+ y = x * sin_delta + y * cos_delta;
+ x = x_new;
+ }
+ gGL.end();
+ }
+ gGL.popUIMatrix();
+}
+
+// Renders a ring with sides (tube shape)
+void gl_deep_circle( F32 radius, F32 depth, S32 steps )
+{
+ F32 x = radius;
+ F32 y = 0.f;
+ F32 angle_delta = F_TWO_PI / (F32)steps;
+ gGL.begin( LLRender::TRIANGLE_STRIP );
+ {
+ S32 step = steps + 1; // An extra step to close the circle.
+ while( step-- )
+ {
+ gGL.vertex3f( x, y, depth );
+ gGL.vertex3f( x, y, 0.f );
+
+ F32 x_new = x * cosf(angle_delta) - y * sinf(angle_delta);
+ y = x * sinf(angle_delta) + y * cosf(angle_delta);
+ x = x_new;
+ }
+ }
+ gGL.end();
+}
+
+void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color, S32 steps, bool render_center )
+{
+ gGL.pushUIMatrix();
+ {
+ gGL.translateUI(0.f, 0.f, -width / 2);
+ if( render_center )
+ {
+ gGL.color4fv(center_color.mV);
+ gGL.diffuseColor4fv(center_color.mV);
+ gl_deep_circle( radius, width, steps );
+ }
+ else
+ {
+ gGL.diffuseColor4fv(side_color.mV);
+ gl_washer_2d(radius, radius - width, steps, side_color, side_color);
+ gGL.translateUI(0.f, 0.f, width);
+ gl_washer_2d(radius - width, radius, steps, side_color, side_color);
+ }
+ }
+ gGL.popUIMatrix();
+}
+
+// Draw gray and white checkerboard with black border
+void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha)
+{
+ //polygon stipple is deprecated, use "Checker" texture
+ LLPointer<LLUIImage> img = LLRender2D::getInstance()->getUIImage("Checker");
+ gGL.getTexUnit(0)->bind(img->getImage());
+ gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_WRAP);
+ gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
+
+ LLColor4 color(1.f, 1.f, 1.f, alpha);
+ LLRectf uv_rect(0, 0, rect.getWidth()/32.f, rect.getHeight()/32.f);
+
+ gl_draw_scaled_image(rect.mLeft, rect.mBottom, rect.getWidth(), rect.getHeight(), img->getImage(), color, uv_rect);
+
+ gGL.flush();
+}
+
+
+// Draws the area between two concentric circles, like
+// a doughnut or washer.
+void gl_washer_2d(F32 outer_radius, F32 inner_radius, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color)
+{
+ const F32 DELTA = F_TWO_PI / steps;
+ const F32 SIN_DELTA = sin( DELTA );
+ const F32 COS_DELTA = cos( DELTA );
+
+ F32 x1 = outer_radius;
+ F32 y1 = 0.f;
+ F32 x2 = inner_radius;
+ F32 y2 = 0.f;
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ gGL.begin( LLRender::TRIANGLE_STRIP );
+ {
+ steps += 1; // An extra step to close the circle.
+ while( steps-- )
+ {
+ gGL.color4fv(outer_color.mV);
+ gGL.vertex2f( x1, y1 );
+ gGL.color4fv(inner_color.mV);
+ gGL.vertex2f( x2, y2 );
+
+ F32 x1_new = x1 * COS_DELTA - y1 * SIN_DELTA;
+ y1 = x1 * SIN_DELTA + y1 * COS_DELTA;
+ x1 = x1_new;
+
+ F32 x2_new = x2 * COS_DELTA - y2 * SIN_DELTA;
+ y2 = x2 * SIN_DELTA + y2 * COS_DELTA;
+ x2 = x2_new;
+ }
+ }
+ gGL.end();
+}
+
+// Draws the area between two concentric circles, like
+// a doughnut or washer.
+void gl_washer_segment_2d(F32 outer_radius, F32 inner_radius, F32 start_radians, F32 end_radians, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color)
+{
+ const F32 DELTA = (end_radians - start_radians) / steps;
+ const F32 SIN_DELTA = sin( DELTA );
+ const F32 COS_DELTA = cos( DELTA );
+
+ F32 x1 = outer_radius * cos( start_radians );
+ F32 y1 = outer_radius * sin( start_radians );
+ F32 x2 = inner_radius * cos( start_radians );
+ F32 y2 = inner_radius * sin( start_radians );
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.begin( LLRender::TRIANGLE_STRIP );
+ {
+ steps += 1; // An extra step to close the circle.
+ while( steps-- )
+ {
+ gGL.color4fv(outer_color.mV);
+ gGL.vertex2f( x1, y1 );
+ gGL.color4fv(inner_color.mV);
+ gGL.vertex2f( x2, y2 );
+
+ F32 x1_new = x1 * COS_DELTA - y1 * SIN_DELTA;
+ y1 = x1 * SIN_DELTA + y1 * COS_DELTA;
+ x1 = x1_new;
+
+ F32 x2_new = x2 * COS_DELTA - y2 * SIN_DELTA;
+ y2 = x2 * SIN_DELTA + y2 * COS_DELTA;
+ x2 = x2_new;
+ }
+ }
+ gGL.end();
+}
+
+void gl_rect_2d_simple_tex( S32 width, S32 height )
+{
+ gGL.begin( LLRender::QUADS );
+
+ gGL.texCoord2f(1.f, 1.f);
+ gGL.vertex2i(width, height);
+
+ gGL.texCoord2f(0.f, 1.f);
+ gGL.vertex2i(0, height);
+
+ gGL.texCoord2f(0.f, 0.f);
+ gGL.vertex2i(0, 0);
+
+ gGL.texCoord2f(1.f, 0.f);
+ gGL.vertex2i(width, 0);
+
+ gGL.end();
+}
+
+void gl_rect_2d_simple( S32 width, S32 height )
+{
+ gGL.begin( LLRender::QUADS );
+ gGL.vertex2i(width, height);
+ gGL.vertex2i(0, height);
+ gGL.vertex2i(0, 0);
+ gGL.vertex2i(width, 0);
+ gGL.end();
+}
+
+void gl_segmented_rect_2d_tex(const S32 left,
+ const S32 top,
+ const S32 right,
+ const S32 bottom,
+ const S32 texture_width,
+ const S32 texture_height,
+ const S32 border_size,
+ const U32 edges)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ S32 width = llabs(right - left);
+ S32 height = llabs(top - bottom);
+
+ gGL.pushUIMatrix();
+
+ gGL.translateUI((F32)left, (F32)bottom, 0.f);
+ LLVector2 border_uv_scale((F32)border_size / (F32)texture_width, (F32)border_size / (F32)texture_height);
+
+ if (border_uv_scale.mV[VX] > 0.5f)
+ {
+ border_uv_scale *= 0.5f / border_uv_scale.mV[VX];
+ }
+ if (border_uv_scale.mV[VY] > 0.5f)
+ {
+ border_uv_scale *= 0.5f / border_uv_scale.mV[VY];
+ }
+
+ F32 border_scale = llmin((F32)border_size, (F32)width * 0.5f, (F32)height * 0.5f);
+ LLVector2 border_width_left = ((edges & (~(U32)ROUNDED_RECT_RIGHT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
+ LLVector2 border_width_right = ((edges & (~(U32)ROUNDED_RECT_LEFT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
+ LLVector2 border_height_bottom = ((edges & (~(U32)ROUNDED_RECT_TOP)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
+ LLVector2 border_height_top = ((edges & (~(U32)ROUNDED_RECT_BOTTOM)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
+ LLVector2 width_vec((F32)width, 0.f);
+ LLVector2 height_vec(0.f, (F32)height);
+
+ gGL.begin(LLRender::QUADS);
+ {
+ // draw bottom left
+ gGL.texCoord2f(0.f, 0.f);
+ gGL.vertex2f(0.f, 0.f);
+
+ gGL.texCoord2f(border_uv_scale.mV[VX], 0.f);
+ gGL.vertex2fv(border_width_left.mV);
+
+ gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ gGL.vertex2fv((border_width_left + border_height_bottom).mV);
+
+ gGL.texCoord2f(0.f, border_uv_scale.mV[VY]);
+ gGL.vertex2fv(border_height_bottom.mV);
+
+ // draw bottom middle
+ gGL.texCoord2f(border_uv_scale.mV[VX], 0.f);
+ gGL.vertex2fv(border_width_left.mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 0.f);
+ gGL.vertex2fv((width_vec - border_width_right).mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ gGL.vertex2fv((width_vec - border_width_right + border_height_bottom).mV);
+
+ gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ gGL.vertex2fv((border_width_left + border_height_bottom).mV);
+
+ // draw bottom right
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 0.f);
+ gGL.vertex2fv((width_vec - border_width_right).mV);
+
+ gGL.texCoord2f(1.f, 0.f);
+ gGL.vertex2fv(width_vec.mV);
+
+ gGL.texCoord2f(1.f, border_uv_scale.mV[VY]);
+ gGL.vertex2fv((width_vec + border_height_bottom).mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ gGL.vertex2fv((width_vec - border_width_right + border_height_bottom).mV);
+
+ // draw left
+ gGL.texCoord2f(0.f, border_uv_scale.mV[VY]);
+ gGL.vertex2fv(border_height_bottom.mV);
+
+ gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ gGL.vertex2fv((border_width_left + border_height_bottom).mV);
+
+ gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((border_width_left + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(0.f, 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((height_vec - border_height_top).mV);
+
+ // draw middle
+ gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ gGL.vertex2fv((border_width_left + border_height_bottom).mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ gGL.vertex2fv((width_vec - border_width_right + border_height_bottom).mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((border_width_left + height_vec - border_height_top).mV);
+
+ // draw right
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ gGL.vertex2fv((width_vec - border_width_right + border_height_bottom).mV);
+
+ gGL.texCoord2f(1.f, border_uv_scale.mV[VY]);
+ gGL.vertex2fv((width_vec + border_height_bottom).mV);
+
+ gGL.texCoord2f(1.f, 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((width_vec + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
+
+ // draw top left
+ gGL.texCoord2f(0.f, 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((border_width_left + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(border_uv_scale.mV[VX], 1.f);
+ gGL.vertex2fv((border_width_left + height_vec).mV);
+
+ gGL.texCoord2f(0.f, 1.f);
+ gGL.vertex2fv((height_vec).mV);
+
+ // draw top middle
+ gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((border_width_left + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f);
+ gGL.vertex2fv((width_vec - border_width_right + height_vec).mV);
+
+ gGL.texCoord2f(border_uv_scale.mV[VX], 1.f);
+ gGL.vertex2fv((border_width_left + height_vec).mV);
+
+ // draw top right
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(1.f, 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((width_vec + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(1.f, 1.f);
+ gGL.vertex2fv((width_vec + height_vec).mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f);
+ gGL.vertex2fv((width_vec - border_width_right + height_vec).mV);
+ }
+ gGL.end();
+
+ gGL.popUIMatrix();
+}
+
+void gl_segmented_rect_2d_fragment_tex(const LLRect& rect,
+ const S32 texture_width,
+ const S32 texture_height,
+ const S32 border_size,
+ const F32 start_fragment,
+ const F32 end_fragment,
+ const U32 edges)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ const S32 left = rect.mLeft;
+ const S32 right = rect.mRight;
+ const S32 top = rect.mTop;
+ const S32 bottom = rect.mBottom;
+ S32 width = llabs(right - left);
+ S32 height = llabs(top - bottom);
+
+ gGL.pushUIMatrix();
+
+ gGL.translateUI((F32)left, (F32)bottom, 0.f);
+ LLVector2 border_uv_scale((F32)border_size / (F32)texture_width, (F32)border_size / (F32)texture_height);
+
+ if (border_uv_scale.mV[VX] > 0.5f)
+ {
+ border_uv_scale *= 0.5f / border_uv_scale.mV[VX];
+ }
+ if (border_uv_scale.mV[VY] > 0.5f)
+ {
+ border_uv_scale *= 0.5f / border_uv_scale.mV[VY];
+ }
+
+ F32 border_scale = llmin((F32)border_size, (F32)width * 0.5f, (F32)height * 0.5f);
+ LLVector2 border_width_left = ((edges & (~(U32)ROUNDED_RECT_RIGHT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
+ LLVector2 border_width_right = ((edges & (~(U32)ROUNDED_RECT_LEFT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
+ LLVector2 border_height_bottom = ((edges & (~(U32)ROUNDED_RECT_TOP)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
+ LLVector2 border_height_top = ((edges & (~(U32)ROUNDED_RECT_BOTTOM)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
+ LLVector2 width_vec((F32)width, 0.f);
+ LLVector2 height_vec(0.f, (F32)height);
+
+ F32 middle_start = border_scale / (F32)width;
+ F32 middle_end = 1.f - middle_start;
+
+ F32 u_min;
+ F32 u_max;
+ LLVector2 x_min;
+ LLVector2 x_max;
+
+ gGL.begin(LLRender::QUADS);
+ {
+ if (start_fragment < middle_start)
+ {
+ u_min = (start_fragment / middle_start) * border_uv_scale.mV[VX];
+ u_max = llmin(end_fragment / middle_start, 1.f) * border_uv_scale.mV[VX];
+ x_min = (start_fragment / middle_start) * border_width_left;
+ x_max = llmin(end_fragment / middle_start, 1.f) * border_width_left;
+
+ // draw bottom left
+ gGL.texCoord2f(u_min, 0.f);
+ gGL.vertex2fv(x_min.mV);
+
+ gGL.texCoord2f(border_uv_scale.mV[VX], 0.f);
+ gGL.vertex2fv(x_max.mV);
+
+ gGL.texCoord2f(u_max, border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_max + border_height_bottom).mV);
+
+ gGL.texCoord2f(u_min, border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_min + border_height_bottom).mV);
+
+ // draw left
+ gGL.texCoord2f(u_min, border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_min + border_height_bottom).mV);
+
+ gGL.texCoord2f(u_max, border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_max + border_height_bottom).mV);
+
+ gGL.texCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
+
+ // draw top left
+ gGL.texCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(u_max, 1.f);
+ gGL.vertex2fv((x_max + height_vec).mV);
+
+ gGL.texCoord2f(u_min, 1.f);
+ gGL.vertex2fv((x_min + height_vec).mV);
+ }
+
+ if (end_fragment > middle_start || start_fragment < middle_end)
+ {
+ x_min = border_width_left + ((llclamp(start_fragment, middle_start, middle_end) - middle_start)) * width_vec;
+ x_max = border_width_left + ((llclamp(end_fragment, middle_start, middle_end) - middle_start)) * width_vec;
+
+ // draw bottom middle
+ gGL.texCoord2f(border_uv_scale.mV[VX], 0.f);
+ gGL.vertex2fv(x_min.mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 0.f);
+ gGL.vertex2fv((x_max).mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_max + border_height_bottom).mV);
+
+ gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_min + border_height_bottom).mV);
+
+ // draw middle
+ gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_min + border_height_bottom).mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_max + border_height_bottom).mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
+
+ // draw top middle
+ gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f);
+ gGL.vertex2fv((x_max + height_vec).mV);
+
+ gGL.texCoord2f(border_uv_scale.mV[VX], 1.f);
+ gGL.vertex2fv((x_min + height_vec).mV);
+ }
+
+ if (end_fragment > middle_end)
+ {
+ u_min = 1.f - ((1.f - llmax(0.f, (start_fragment - middle_end) / middle_start)) * border_uv_scale.mV[VX]);
+ u_max = 1.f - ((1.f - ((end_fragment - middle_end) / middle_start)) * border_uv_scale.mV[VX]);
+ x_min = width_vec - ((1.f - llmax(0.f, (start_fragment - middle_end) / middle_start)) * border_width_right);
+ x_max = width_vec - ((1.f - ((end_fragment - middle_end) / middle_start)) * border_width_right);
+
+ // draw bottom right
+ gGL.texCoord2f(u_min, 0.f);
+ gGL.vertex2fv((x_min).mV);
+
+ gGL.texCoord2f(u_max, 0.f);
+ gGL.vertex2fv(x_max.mV);
+
+ gGL.texCoord2f(u_max, border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_max + border_height_bottom).mV);
+
+ gGL.texCoord2f(u_min, border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_min + border_height_bottom).mV);
+
+ // draw right
+ gGL.texCoord2f(u_min, border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_min + border_height_bottom).mV);
+
+ gGL.texCoord2f(u_max, border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_max + border_height_bottom).mV);
+
+ gGL.texCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
+
+ // draw top right
+ gGL.texCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
+ gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
+
+ gGL.texCoord2f(u_max, 1.f);
+ gGL.vertex2fv((x_max + height_vec).mV);
+
+ gGL.texCoord2f(u_min, 1.f);
+ gGL.vertex2fv((x_min + height_vec).mV);
+ }
+ }
+ gGL.end();
+
+ gGL.popUIMatrix();
+}
+
+void gl_segmented_rect_3d_tex(const LLRectf& clip_rect, const LLRectf& center_uv_rect, const LLRectf& center_draw_rect,
+ const LLVector3& width_vec, const LLVector3& height_vec)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ gGL.begin(LLRender::QUADS);
+ {
+ // draw bottom left
+ gGL.texCoord2f(clip_rect.mLeft, clip_rect.mBottom);
+ gGL.vertex3f(0.f, 0.f, 0.f);
+
+ gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV);
+
+ gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mBottom * height_vec).mV);
+
+ // draw bottom middle
+ gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV);
+
+ // draw bottom right
+ gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec).mV);
+
+ gGL.texCoord2f(clip_rect.mRight, clip_rect.mBottom);
+ gGL.vertex3fv(width_vec.mV);
+
+ gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mBottom);
+ gGL.vertex3fv((width_vec + center_draw_rect.mBottom * height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV);
+
+ // draw left
+ gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mBottom * height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV);
+
+ gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mTop * height_vec).mV);
+
+ // draw middle
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV);
+
+ // draw right
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV);
+
+ gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mBottom);
+ gGL.vertex3fv((width_vec + center_draw_rect.mBottom * height_vec).mV);
+
+ gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mTop);
+ gGL.vertex3fv((width_vec + center_draw_rect.mTop * height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV);
+
+ // draw top left
+ gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mTop * height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV);
+
+ gGL.texCoord2f(clip_rect.mLeft, clip_rect.mTop);
+ gGL.vertex3fv((height_vec).mV);
+
+ // draw top middle
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV);
+
+ // draw top right
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV);
+
+ gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mTop);
+ gGL.vertex3fv((width_vec + center_draw_rect.mTop * height_vec).mV);
+
+ gGL.texCoord2f(clip_rect.mRight, clip_rect.mTop);
+ gGL.vertex3fv((width_vec + height_vec).mV);
+
+ gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + height_vec).mV);
+ }
+ gGL.end();
+
+}
+
+LLRender2D::LLRender2D(LLImageProviderInterface* image_provider)
+{
+ mImageProvider = image_provider;
+ if(mImageProvider)
+ {
+ mImageProvider->addOnRemovalCallback(resetProvider);
+ }
+}
+
+LLRender2D::~LLRender2D()
+{
+ if(mImageProvider)
+ {
+ mImageProvider->cleanUp();
+ mImageProvider->deleteOnRemovalCallback(resetProvider);
+ }
+}
+
+// static
+void LLRender2D::translate(F32 x, F32 y, F32 z)
+{
+ gGL.translateUI(x,y,z);
+ LLFontGL::sCurOrigin.mX += (S32) x;
+ LLFontGL::sCurOrigin.mY += (S32) y;
+ LLFontGL::sCurDepth += z;
+}
+
+// static
+void LLRender2D::pushMatrix()
+{
+ gGL.pushUIMatrix();
+ LLFontGL::sOriginStack.push_back(std::make_pair(LLFontGL::sCurOrigin, LLFontGL::sCurDepth));
+}
+
+// static
+void LLRender2D::popMatrix()
+{
+ gGL.popUIMatrix();
+ LLFontGL::sCurOrigin = LLFontGL::sOriginStack.back().first;
+ LLFontGL::sCurDepth = LLFontGL::sOriginStack.back().second;
+ LLFontGL::sOriginStack.pop_back();
+}
+
+// static
+void LLRender2D::loadIdentity()
+{
+ gGL.loadUIIdentity();
+ LLFontGL::sCurOrigin.mX = 0;
+ LLFontGL::sCurOrigin.mY = 0;
+ LLFontGL::sCurDepth = 0.f;
+}
+
+// static
+void LLRender2D::setLineWidth(F32 width)
+{
+ gGL.flush();
+ // If outside the allowed range, glLineWidth fails with "invalid value".
+ // On Darwin, the range is [1, 1].
+ static GLfloat range[2]{0.0};
+ if (range[1] == 0)
+ {
+ glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, range);
+ }
+ width *= lerp(LLRender::sUIGLScaleFactor.mV[VX], LLRender::sUIGLScaleFactor.mV[VY], 0.5f);
+ glLineWidth(llclamp(width, range[0], range[1]));
+}
+
+LLPointer<LLUIImage> LLRender2D::getUIImageByID(const LLUUID& image_id, S32 priority)
+{
+ if (mImageProvider)
+ {
+ return mImageProvider->getUIImageByID(image_id, priority);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+LLPointer<LLUIImage> LLRender2D::getUIImage(const std::string& name, S32 priority)
+{
+ if (!name.empty() && mImageProvider)
+ return mImageProvider->getUIImage(name, priority);
+ else
+ return NULL;
+}
+
+// static
+void LLRender2D::resetProvider()
+{
+ if (LLRender2D::instanceExists())
+ {
+ LLRender2D::getInstance()->mImageProvider = NULL;
+ }
+}
+
+// class LLImageProviderInterface
+
+LLImageProviderInterface::~LLImageProviderInterface()
+{
+ for (callback_list_t::iterator iter = mCallbackList.begin(); iter != mCallbackList.end();)
+ {
+ callback_list_t::iterator curiter = iter++;
+ (*curiter)();
+ }
+}
+
+void LLImageProviderInterface::addOnRemovalCallback(callback_t func)
+{
+ if (!func)
+ {
+ return;
+ }
+ mCallbackList.push_back(func);
+}
+
+void LLImageProviderInterface::deleteOnRemovalCallback(callback_t func)
+{
+ callback_list_t::iterator iter = std::find(mCallbackList.begin(), mCallbackList.end(), func);
+ if (iter != mCallbackList.end())
+ {
+ mCallbackList.erase(iter);
+ }
+}
+
diff --git a/indra/llrender/llrender2dutils.h b/indra/llrender/llrender2dutils.h
index 25a9002e72..0d3efc38d6 100644
--- a/indra/llrender/llrender2dutils.h
+++ b/indra/llrender/llrender2dutils.h
@@ -1,177 +1,177 @@
-/**
- * @file llrender2dutils.h
- * @brief GL function declarations for immediate-mode gl drawing.
- *
- * $LicenseInfo:firstyear=2012&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$
- */
-
-// All immediate-mode gl drawing should happen here.
-
-
-#ifndef LL_RENDER2DUTILS_H
-#define LL_RENDER2DUTILS_H
-
-#include "llpointer.h" // LLPointer<>
-#include "llrect.h"
-#include "llsingleton.h"
-#include "llglslshader.h"
-
-class LLColor4;
-class LLVector3;
-class LLVector2;
-class LLUIImage;
-class LLUUID;
-
-extern const LLColor4 UI_VERTEX_COLOR;
-
-bool ui_point_in_rect(S32 x, S32 y, S32 left, S32 top, S32 right, S32 bottom);
-void gl_state_for_2d(S32 width, S32 height);
-
-void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2);
-void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2, const LLColor4 &color );
-void gl_triangle_2d(S32 x1, S32 y1, S32 x2, S32 y2, S32 x3, S32 y3, const LLColor4& color, bool filled);
-void gl_rect_2d_simple( S32 width, S32 height );
-
-void gl_draw_x(const LLRect& rect, const LLColor4& color);
-
-void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, bool filled = true );
-void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, bool filled = true );
-void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, S32 pixel_offset = 0, bool filled = true );
-void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixel_offset = 0, bool filled = true );
-void gl_rect_2d(const LLRect& rect, bool filled = true );
-void gl_rect_2d(const LLRect& rect, const LLColor4& color, bool filled = true );
-void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha = 1.0f);
-
-void gl_drop_shadow(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &start_color, S32 lines);
-
-void gl_circle_2d(F32 x, F32 y, F32 radius, S32 steps, bool filled);
-void gl_arc_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, bool filled, F32 start_angle, F32 end_angle);
-void gl_deep_circle( F32 radius, F32 depth );
-void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color, S32 steps, bool render_center );
-void gl_corners_2d(S32 left, S32 top, S32 right, S32 bottom, S32 length, F32 max_frac);
-void gl_washer_2d(F32 outer_radius, F32 inner_radius, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color);
-void gl_washer_segment_2d(F32 outer_radius, F32 inner_radius, F32 start_radians, F32 end_radians, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color);
-
-void gl_draw_image(S32 x, S32 y, LLTexture* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
-void gl_draw_scaled_target(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* target, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
-void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLTexture* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
-void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLTexture* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
-void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees, LLTexture* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f), LLRenderTarget* target = NULL);
-void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLTexture* image, const LLColor4 &color, bool solid_color = false, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f), bool scale_inner = true);
-void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTexture* image, const LLColor4 &color, bool solid_color = false, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f), const LLRectf& scale_rect = LLRectf(0.f, 1.f, 1.f, 0.f), bool scale_inner = true);
-
-void gl_line_3d( const LLVector3& start, const LLVector3& end, const LLColor4& color);
-
-void gl_rect_2d_simple_tex( S32 width, S32 height );
-
-// segmented rectangles
-
-/*
- TL |______TOP_________| TR
- /| |\
- _/_|__________________|_\_
- L| | MIDDLE | |R
- _|_|__________________|_|_
- \ | BOTTOM | /
- BL\|__________________|/ BR
- | |
-*/
-
-typedef enum e_rounded_edge
-{
- ROUNDED_RECT_LEFT = 0x1,
- ROUNDED_RECT_TOP = 0x2,
- ROUNDED_RECT_RIGHT = 0x4,
- ROUNDED_RECT_BOTTOM = 0x8,
- ROUNDED_RECT_ALL = 0xf
-}ERoundedEdge;
-
-
-void gl_segmented_rect_2d_tex(const S32 left, const S32 top, const S32 right, const S32 bottom, const S32 texture_width, const S32 texture_height, const S32 border_size, const U32 edges = ROUNDED_RECT_ALL);
-void gl_segmented_rect_2d_fragment_tex(const LLRect& rect, const S32 texture_width, const S32 texture_height, const S32 border_size, const F32 start_fragment, const F32 end_fragment, const U32 edges = ROUNDED_RECT_ALL);
-void gl_segmented_rect_3d_tex(const LLRectf& clip_rect, const LLRectf& center_uv_rect, const LLRectf& center_draw_rect, const LLVector3& width_vec, const LLVector3& height_vec);
-
-inline void gl_rect_2d( const LLRect& rect, bool filled )
-{
- gl_rect_2d( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, filled );
-}
-
-inline void gl_rect_2d_offset_local( const LLRect& rect, S32 pixel_offset, bool filled)
-{
- gl_rect_2d_offset_local( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, pixel_offset, filled );
-}
-
-class LLImageProviderInterface;
-
-class LLRender2D : public LLParamSingleton<LLRender2D>
-{
- LLSINGLETON(LLRender2D, LLImageProviderInterface* image_provider);
- LOG_CLASS(LLRender2D);
- ~LLRender2D();
-public:
- static void pushMatrix();
- static void popMatrix();
- static void loadIdentity();
- static void translate(F32 x, F32 y, F32 z = 0.0f);
-
- static void setLineWidth(F32 width);
-
- LLPointer<LLUIImage> getUIImageByID(const LLUUID& image_id, S32 priority = 0);
- LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority = 0);
-
-protected:
- // since LLRender2D has no control of image provider's lifecycle
- // we need a way to tell LLRender2D that provider died and
- // LLRender2D needs to be updated.
- static void resetProvider();
-
-private:
- LLImageProviderInterface* mImageProvider;
-};
-
-class LLImageProviderInterface
-{
-protected:
- LLImageProviderInterface() {};
- virtual ~LLImageProviderInterface();
-public:
- virtual LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority) = 0;
- virtual LLPointer<LLUIImage> getUIImageByID(const LLUUID& id, S32 priority) = 0;
- virtual void cleanUp() = 0;
-
- // to notify holders when pointer gets deleted
- typedef void(*callback_t)();
- void addOnRemovalCallback(callback_t func);
- void deleteOnRemovalCallback(callback_t func);
-
-private:
-
- typedef std::list< callback_t > callback_list_t;
- callback_list_t mCallbackList;
-};
-
-
-extern LLGLSLShader gSolidColorProgram;
-extern LLGLSLShader gUIProgram;
-
-#endif // LL_RENDER2DUTILS_H
-
+/**
+ * @file llrender2dutils.h
+ * @brief GL function declarations for immediate-mode gl drawing.
+ *
+ * $LicenseInfo:firstyear=2012&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$
+ */
+
+// All immediate-mode gl drawing should happen here.
+
+
+#ifndef LL_RENDER2DUTILS_H
+#define LL_RENDER2DUTILS_H
+
+#include "llpointer.h" // LLPointer<>
+#include "llrect.h"
+#include "llsingleton.h"
+#include "llglslshader.h"
+
+class LLColor4;
+class LLVector3;
+class LLVector2;
+class LLUIImage;
+class LLUUID;
+
+extern const LLColor4 UI_VERTEX_COLOR;
+
+bool ui_point_in_rect(S32 x, S32 y, S32 left, S32 top, S32 right, S32 bottom);
+void gl_state_for_2d(S32 width, S32 height);
+
+void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2);
+void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2, const LLColor4 &color );
+void gl_triangle_2d(S32 x1, S32 y1, S32 x2, S32 y2, S32 x3, S32 y3, const LLColor4& color, bool filled);
+void gl_rect_2d_simple( S32 width, S32 height );
+
+void gl_draw_x(const LLRect& rect, const LLColor4& color);
+
+void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, bool filled = true );
+void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, bool filled = true );
+void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, S32 pixel_offset = 0, bool filled = true );
+void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixel_offset = 0, bool filled = true );
+void gl_rect_2d(const LLRect& rect, bool filled = true );
+void gl_rect_2d(const LLRect& rect, const LLColor4& color, bool filled = true );
+void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha = 1.0f);
+
+void gl_drop_shadow(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &start_color, S32 lines);
+
+void gl_circle_2d(F32 x, F32 y, F32 radius, S32 steps, bool filled);
+void gl_arc_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, bool filled, F32 start_angle, F32 end_angle);
+void gl_deep_circle( F32 radius, F32 depth );
+void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color, S32 steps, bool render_center );
+void gl_corners_2d(S32 left, S32 top, S32 right, S32 bottom, S32 length, F32 max_frac);
+void gl_washer_2d(F32 outer_radius, F32 inner_radius, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color);
+void gl_washer_segment_2d(F32 outer_radius, F32 inner_radius, F32 start_radians, F32 end_radians, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color);
+
+void gl_draw_image(S32 x, S32 y, LLTexture* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
+void gl_draw_scaled_target(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* target, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
+void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLTexture* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
+void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLTexture* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
+void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees, LLTexture* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f), LLRenderTarget* target = NULL);
+void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLTexture* image, const LLColor4 &color, bool solid_color = false, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f), bool scale_inner = true);
+void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTexture* image, const LLColor4 &color, bool solid_color = false, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f), const LLRectf& scale_rect = LLRectf(0.f, 1.f, 1.f, 0.f), bool scale_inner = true);
+
+void gl_line_3d( const LLVector3& start, const LLVector3& end, const LLColor4& color);
+
+void gl_rect_2d_simple_tex( S32 width, S32 height );
+
+// segmented rectangles
+
+/*
+ TL |______TOP_________| TR
+ /| |\
+ _/_|__________________|_\_
+ L| | MIDDLE | |R
+ _|_|__________________|_|_
+ \ | BOTTOM | /
+ BL\|__________________|/ BR
+ | |
+*/
+
+typedef enum e_rounded_edge
+{
+ ROUNDED_RECT_LEFT = 0x1,
+ ROUNDED_RECT_TOP = 0x2,
+ ROUNDED_RECT_RIGHT = 0x4,
+ ROUNDED_RECT_BOTTOM = 0x8,
+ ROUNDED_RECT_ALL = 0xf
+}ERoundedEdge;
+
+
+void gl_segmented_rect_2d_tex(const S32 left, const S32 top, const S32 right, const S32 bottom, const S32 texture_width, const S32 texture_height, const S32 border_size, const U32 edges = ROUNDED_RECT_ALL);
+void gl_segmented_rect_2d_fragment_tex(const LLRect& rect, const S32 texture_width, const S32 texture_height, const S32 border_size, const F32 start_fragment, const F32 end_fragment, const U32 edges = ROUNDED_RECT_ALL);
+void gl_segmented_rect_3d_tex(const LLRectf& clip_rect, const LLRectf& center_uv_rect, const LLRectf& center_draw_rect, const LLVector3& width_vec, const LLVector3& height_vec);
+
+inline void gl_rect_2d( const LLRect& rect, bool filled )
+{
+ gl_rect_2d( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, filled );
+}
+
+inline void gl_rect_2d_offset_local( const LLRect& rect, S32 pixel_offset, bool filled)
+{
+ gl_rect_2d_offset_local( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, pixel_offset, filled );
+}
+
+class LLImageProviderInterface;
+
+class LLRender2D : public LLParamSingleton<LLRender2D>
+{
+ LLSINGLETON(LLRender2D, LLImageProviderInterface* image_provider);
+ LOG_CLASS(LLRender2D);
+ ~LLRender2D();
+public:
+ static void pushMatrix();
+ static void popMatrix();
+ static void loadIdentity();
+ static void translate(F32 x, F32 y, F32 z = 0.0f);
+
+ static void setLineWidth(F32 width);
+
+ LLPointer<LLUIImage> getUIImageByID(const LLUUID& image_id, S32 priority = 0);
+ LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority = 0);
+
+protected:
+ // since LLRender2D has no control of image provider's lifecycle
+ // we need a way to tell LLRender2D that provider died and
+ // LLRender2D needs to be updated.
+ static void resetProvider();
+
+private:
+ LLImageProviderInterface* mImageProvider;
+};
+
+class LLImageProviderInterface
+{
+protected:
+ LLImageProviderInterface() {};
+ virtual ~LLImageProviderInterface();
+public:
+ virtual LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority) = 0;
+ virtual LLPointer<LLUIImage> getUIImageByID(const LLUUID& id, S32 priority) = 0;
+ virtual void cleanUp() = 0;
+
+ // to notify holders when pointer gets deleted
+ typedef void(*callback_t)();
+ void addOnRemovalCallback(callback_t func);
+ void deleteOnRemovalCallback(callback_t func);
+
+private:
+
+ typedef std::list< callback_t > callback_list_t;
+ callback_list_t mCallbackList;
+};
+
+
+extern LLGLSLShader gSolidColorProgram;
+extern LLGLSLShader gUIProgram;
+
+#endif // LL_RENDER2DUTILS_H
+
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index 82f063d9c4..98bfb38f03 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -1,1423 +1,1423 @@
-/**
- * @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 "llrender.h"
-#include "llfile.h"
-#include "lldir.h"
-#include "llsdutil.h"
-#include "llsdserialize.h"
-#include "hbxxh.h"
-
-#if LL_DARWIN
-#include "OpenGL/OpenGL.h"
-#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;
-
- if (features->attachNothing)
- {
- return true;
- }
- //////////////////////////////////////
- // Attach Vertex Shader Features First
- //////////////////////////////////////
-
- // NOTE order of shader object attaching is VERY IMPORTANT!!!
- if (features->calculatesAtmospherics)
- {
- if (!shader->attachVertexObject("windlight/atmosphericsVarsV.glsl"))
- {
- return false;
- }
- }
-
- if (features->calculatesLighting || features->calculatesAtmospherics)
- {
- if (!shader->attachVertexObject("windlight/atmosphericsHelpersV.glsl"))
- {
- return false;
- }
- }
-
- if (features->calculatesLighting)
- {
- if (features->isSpecular)
- {
- if (!shader->attachVertexObject("lighting/lightFuncSpecularV.glsl"))
- {
- return false;
- }
-
- if (!features->isAlphaLighting)
- {
- if (!shader->attachVertexObject("lighting/sumLightsSpecularV.glsl"))
- {
- return false;
- }
- }
-
- if (!shader->attachVertexObject("lighting/lightSpecularV.glsl"))
- {
- return false;
- }
- }
- else
- {
- if (!shader->attachVertexObject("lighting/lightFuncV.glsl"))
- {
- return false;
- }
-
- if (!features->isAlphaLighting)
- {
- if (!shader->attachVertexObject("lighting/sumLightsV.glsl"))
- {
- return false;
- }
- }
-
- if (!shader->attachVertexObject("lighting/lightV.glsl"))
- {
- return false;
- }
- }
- }
-
- // NOTE order of shader object attaching is VERY IMPORTANT!!!
- if (features->calculatesAtmospherics)
- {
- if (!shader->attachVertexObject("environment/srgbF.glsl")) // NOTE -- "F" suffix is superfluous here, there is nothing fragment specific in srgbF
- {
- return false;
- }
-
- if (!shader->attachVertexObject("windlight/atmosphericsFuncs.glsl")) {
- return false;
- }
-
- if (!shader->attachVertexObject("windlight/atmosphericsV.glsl"))
- {
- return false;
- }
- }
-
- if (features->hasSkinning)
- {
- if (!shader->attachVertexObject("avatar/avatarSkinV.glsl"))
- {
- return false;
- }
- }
-
- if (features->hasObjectSkinning)
- {
- shader->mRiggedVariant = shader;
- if (!shader->attachVertexObject("avatar/objectSkinV.glsl"))
- {
- return false;
- }
- }
-
- if (!shader->attachVertexObject("deferred/textureUtilV.glsl"))
- {
- return false;
- }
-
- ///////////////////////////////////////
- // Attach Fragment Shader Features Next
- ///////////////////////////////////////
-
-// NOTE order of shader object attaching is VERY IMPORTANT!!!
- if (features->hasSrgb || features->hasAtmospherics || features->calculatesAtmospherics || features->isDeferred)
- {
- if (!shader->attachFragmentObject("environment/srgbF.glsl"))
- {
- return false;
- }
- }
-
- if(features->calculatesAtmospherics || features->hasGamma || features->isDeferred)
- {
- if (!shader->attachFragmentObject("windlight/atmosphericsVarsF.glsl"))
- {
- return false;
- }
- }
-
- if (features->calculatesLighting || features->calculatesAtmospherics)
- {
- if (!shader->attachFragmentObject("windlight/atmosphericsHelpersF.glsl"))
- {
- return false;
- }
- }
-
- // we want this BEFORE shadows and AO because those facilities use pos/norm access
- if (features->isDeferred || features->hasReflectionProbes)
- {
- if (!shader->attachFragmentObject("deferred/deferredUtil.glsl"))
- {
- return false;
- }
- }
-
- if (features->hasScreenSpaceReflections || features->hasReflectionProbes)
- {
- if (!shader->attachFragmentObject("deferred/screenSpaceReflUtil.glsl"))
- {
- return false;
- }
- }
-
- if (features->hasShadows)
- {
- if (!shader->attachFragmentObject("deferred/shadowUtil.glsl"))
- {
- return false;
- }
- }
-
- if (features->hasReflectionProbes)
- {
- if (!shader->attachFragmentObject("deferred/reflectionProbeF.glsl"))
- {
- return false;
- }
- }
-
- if (features->hasAmbientOcclusion)
- {
- if (!shader->attachFragmentObject("deferred/aoUtil.glsl"))
- {
- return false;
- }
- }
-
- if (features->hasGamma || features->isDeferred)
- {
- if (!shader->attachFragmentObject("windlight/gammaF.glsl"))
- {
- return false;
- }
- }
-
- if (features->encodesNormal)
- {
- if (!shader->attachFragmentObject("environment/encodeNormF.glsl"))
- {
- return false;
- }
- }
-
- if (features->hasAtmospherics || features->isDeferred)
- {
- if (!shader->attachFragmentObject("windlight/atmosphericsFuncs.glsl")) {
- return false;
- }
-
- if (!shader->attachFragmentObject("windlight/atmosphericsF.glsl"))
- {
- return false;
- }
- }
-
- // NOTE order of shader object attaching is VERY IMPORTANT!!!
- if (features->hasAtmospherics)
- {
- if (!shader->attachFragmentObject("environment/waterFogF.glsl"))
- {
- return false;
- }
- }
-
- if (features->hasLighting)
- {
- if (features->disableTextureIndex)
- {
- if (features->hasAlphaMask)
- {
- if (!shader->attachFragmentObject("lighting/lightAlphaMaskNonIndexedF.glsl"))
- {
- return false;
- }
- }
- else
- {
- if (!shader->attachFragmentObject("lighting/lightNonIndexedF.glsl"))
- {
- return false;
- }
- }
- }
- else
- {
- if (features->hasAlphaMask)
- {
- if (!shader->attachFragmentObject("lighting/lightAlphaMaskF.glsl"))
- {
- return false;
- }
- }
- else
- {
- if (!shader->attachFragmentObject("lighting/lightF.glsl"))
- {
- return false;
- }
- }
- shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels-1, 1);
- }
- }
-
- if (features->mIndexedTextureChannels <= 1)
- {
- if (!shader->attachVertexObject("objects/nonindexedTextureV.glsl"))
- {
- return false;
- }
- }
- else
- {
- if (!shader->attachVertexObject("objects/indexedTextureV.glsl"))
- {
- return false;
- }
- }
-
- return true;
-}
-
-//============================================================================
-// Load Shader
-
-static std::string get_shader_log(GLuint ret)
-{
- std::string res;
-
- //get log length
- GLint length;
- glGetShaderiv(ret, GL_INFO_LOG_LENGTH, &length);
- if (length > 0)
- {
- //the log could be any size, so allocate appropriately
- GLchar* log = new GLchar[length];
- glGetShaderInfoLog(ret, length, &length, log);
- res = std::string((char *)log);
- delete[] log;
- }
- return res;
-}
-
-static std::string get_program_log(GLuint ret)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
- std::string res;
-
- //get log length
- GLint length;
- glGetProgramiv(ret, GL_INFO_LOG_LENGTH, &length);
- if (length > 0)
- {
- //the log could be any size, so allocate appropriately
- GLchar* log = new GLchar[length];
- glGetProgramInfoLog(ret, length, &length, log);
- res = std::string((char*)log);
- delete[] log;
- }
- return res;
-}
-
-// get the info log for the given object, be it a shader or program object
-// NOTE: ret MUST be a shader OR a program object
-static std::string get_object_log(GLuint ret)
-{
- if (glIsProgram(ret))
- {
- return get_program_log(ret);
- }
- else
- {
- llassert(glIsShader(ret));
- return get_shader_log(ret);
- }
-}
-
-//dump shader source for debugging
-void LLShaderMgr::dumpShaderSource(U32 shader_code_count, GLchar** shader_code_text)
-{
- char num_str[16]; // U32 = max 10 digits
-
- LL_SHADER_LOADING_WARNS() << "\n";
-
- for (U32 i = 0; i < shader_code_count; i++)
- {
- snprintf(num_str, sizeof(num_str), "%4d: ", i+1);
- std::string line_number(num_str);
- LL_CONT << line_number << shader_code_text[i];
- }
- LL_CONT << LL_ENDL;
-}
-
-void LLShaderMgr::dumpObjectLog(GLuint ret, bool warns, const std::string& filename)
-{
- std::string log;
- log = get_object_log(ret);
- std::string fname = filename;
- if (filename.empty())
- {
- fname = "unknown shader file";
- }
-
- if (log.length() > 0)
- {
- LL_SHADER_LOADING_WARNS() << "Shader loading from " << fname << LL_ENDL;
- LL_SHADER_LOADING_WARNS() << "\n" << log << LL_ENDL;
- }
- }
-
-GLuint LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type, std::map<std::string, std::string>* defines, S32 texture_index_channels)
-{
-
-// endsure work-around for missing GLSL funcs gets propogated to feature shader files (e.g. srgbF.glsl)
-#if LL_DARWIN
- if (defines)
- {
- (*defines)["OLD_SELECT"] = "1";
- }
-#endif
-
- GLenum error = GL_NO_ERROR;
-
- error = glGetError();
- if (error != GL_NO_ERROR)
- {
- LL_SHADER_LOADING_WARNS() << "GL ERROR entering loadShaderFile(): " << error << " for file: " << filename << LL_ENDL;
- }
-
- if (filename.empty())
- {
- return 0;
- }
-
-
- //read in from file
- LLFILE* file = NULL;
-
- S32 try_gpu_class = shader_level;
- S32 gpu_class;
-
- std::string open_file_name;
-
-#if 0 // WIP -- try to come up with a way to fallback to an error shader without needing debug stubs all over the place in the shader tree
- if (shader_level == -1)
- {
- // use "error" fallback
- if (type == GL_VERTEX_SHADER)
- {
- open_file_name = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "shaders/errorV.glsl");
- }
- else
- {
- llassert(type == GL_FRAGMENT_SHADER); // type must be vertex or fragment shader
- open_file_name = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "shaders/errorF.glsl");
- }
-
- file = LLFile::fopen(open_file_name, "r");
- }
- else
-#endif
- {
- //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;
-
- open_file_name = fname.str();
-
- /*
- Would be awesome, if we didn't have shaders that re-use files
- with different environments to say, add skinning, etc
- can't depend on cached version to have evaluate ifdefs identically...
- if we can define a deterministic hash for the shader based on
- all the inputs, maybe we can save some time here.
- if (mShaderObjects.count(filename) > 0)
- {
- return mShaderObjects[filename];
- }
-
- */
-
- LL_DEBUGS("ShaderLoading") << "Looking in " << open_file_name << LL_ENDL;
- file = LLFile::fopen(open_file_name, "r"); /* Flawfinder: ignore */
- if (file)
- {
- LL_DEBUGS("ShaderLoading") << "Loading file: " << open_file_name << " (Want class " << gpu_class << ")" << LL_ENDL;
- break; // done
- }
- }
- }
-
- if (file == NULL)
- {
- LL_WARNS("ShaderLoading") << "GLSL Shader file not found: " << open_file_name << LL_ENDL;
- return 0;
- }
-
- //we can't have any lines longer than 1024 characters
- //or any shaders longer than 4096 lines... deal - DaveP
- GLchar buff[1024];
- GLchar *extra_code_text[1024];
- GLchar *shader_code_text[4096 + LL_ARRAY_SIZE(extra_code_text)] = { NULL };
- GLuint extra_code_count = 0, shader_code_count = 0;
- BOOST_STATIC_ASSERT(LL_ARRAY_SIZE(extra_code_text) < LL_ARRAY_SIZE(shader_code_text));
-
-
- S32 major_version = gGLManager.mGLSLVersionMajor;
- S32 minor_version = gGLManager.mGLSLVersionMinor;
-
- if (major_version == 1 && minor_version < 30)
- {
- llassert(false); // GL 3.1 or later required
- }
- else
- {
- if (major_version >= 4)
- {
- //set version to 400 or 420
- if (minor_version >= 20)
- {
- shader_code_text[shader_code_count++] = strdup("#version 420\n");
- }
- else
- {
- shader_code_text[shader_code_count++] = strdup("#version 400\n");
- }
- }
- else if (major_version == 3)
- {
- if (minor_version < 10)
- {
- shader_code_text[shader_code_count++] = strdup("#version 300\n");
- }
- else if (minor_version <= 19)
- {
- shader_code_text[shader_code_count++] = strdup("#version 310\n");
- }
- else if (minor_version <= 29)
- {
- shader_code_text[shader_code_count++] = strdup("#version 320\n");
- }
- else
- {
- shader_code_text[shader_code_count++] = strdup("#version 330\n");
- }
- }
- else
- {
- //set version to 1.40
- shader_code_text[shader_code_count++] = strdup("#version 140\n");
- //some implementations of GLSL 1.30 require integer precision be explicitly declared
- extra_code_text[extra_code_count++] = strdup("precision mediump int;\n");
- extra_code_text[extra_code_count++] = strdup("precision highp float;\n");
- }
-
- extra_code_text[extra_code_count++] = strdup("#define FXAA_GLSL_130 1\n");
- }
-
- // Use alpha float to store bit flags
- // See: C++: addDeferredAttachment(), shader: frag_data[2]
- extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_SKIP_ATMOS 0.0 \n"); // atmo kill
- extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_ATMOS 0.34\n"); // bit 0
- extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_PBR 0.67\n"); // bit 1
- extra_code_text[extra_code_count++] = strdup("#define GET_GBUFFER_FLAG(flag) (abs(norm.w-flag)< 0.1)\n");
-
- if (defines)
- {
- for (auto iter = defines->begin(); iter != defines->end(); ++iter)
- {
- std::string define = "#define " + iter->first + " " + iter->second + "\n";
- extra_code_text[extra_code_count++] = (GLchar *) strdup(define.c_str());
- }
- }
-
- if( gGLManager.mIsAMD )
- {
- extra_code_text[extra_code_count++] = strdup( "#define IS_AMD_CARD 1\n" );
- }
-
- if (texture_index_channels > 0 && type == GL_FRAGMENT_SHADER)
- {
- //use specified number of texture channels for indexed texture rendering
-
- /* prepend shader code that looks like this:
-
- uniform sampler2D tex0;
- uniform sampler2D tex1;
- uniform sampler2D tex2;
- .
- .
- .
- uniform sampler2D texN;
-
- flat in int vary_texture_index;
-
- vec4 ret = vec4(1,0,1,1);
-
- vec4 diffuseLookup(vec2 texcoord)
- {
- switch (vary_texture_index)
- {
- case 0: ret = texture(tex0, texcoord); break;
- case 1: ret = texture(tex1, texcoord); break;
- case 2: ret = texture(tex2, texcoord); break;
- .
- .
- .
- case N: return texture(texN, texcoord); break;
- }
-
- return ret;
- }
- */
-
- extra_code_text[extra_code_count++] = strdup("#define HAS_DIFFUSE_LOOKUP\n");
-
- //uniform declartion
- for (S32 i = 0; i < texture_index_channels; ++i)
- {
- std::string decl = llformat("uniform sampler2D tex%d;\n", i);
- extra_code_text[extra_code_count++] = strdup(decl.c_str());
- }
-
- if (texture_index_channels > 1)
- {
- extra_code_text[extra_code_count++] = strdup("flat in int vary_texture_index;\n");
- }
-
- extra_code_text[extra_code_count++] = strdup("vec4 diffuseLookup(vec2 texcoord)\n");
- extra_code_text[extra_code_count++] = strdup("{\n");
-
-
- if (texture_index_channels == 1)
- { //don't use flow control, that's silly
- extra_code_text[extra_code_count++] = strdup("return texture(tex0, texcoord);\n");
- extra_code_text[extra_code_count++] = strdup("}\n");
- }
- else if (major_version > 1 || minor_version >= 30)
- { //switches are supported in GLSL 1.30 and later
- if (gGLManager.mIsNVIDIA)
- { //switches are unreliable on some NVIDIA drivers
- for (U32 i = 0; i < texture_index_channels; ++i)
- {
- std::string if_string = llformat("\t%sif (vary_texture_index == %d) { return texture(tex%d, texcoord); }\n", i > 0 ? "else " : "", i, i);
- extra_code_text[extra_code_count++] = strdup(if_string.c_str());
- }
- extra_code_text[extra_code_count++] = strdup("\treturn vec4(1,0,1,1);\n");
- extra_code_text[extra_code_count++] = strdup("}\n");
- }
- else
- {
- extra_code_text[extra_code_count++] = strdup("\tvec4 ret = vec4(1,0,1,1);\n");
- extra_code_text[extra_code_count++] = strdup("\tswitch (vary_texture_index)\n");
- extra_code_text[extra_code_count++] = strdup("\t{\n");
-
- //switch body
- for (S32 i = 0; i < texture_index_channels; ++i)
- {
- std::string case_str = llformat("\t\tcase %d: return texture(tex%d, texcoord);\n", i, i);
- extra_code_text[extra_code_count++] = strdup(case_str.c_str());
- }
-
- extra_code_text[extra_code_count++] = strdup("\t}\n");
- extra_code_text[extra_code_count++] = strdup("\treturn ret;\n");
- extra_code_text[extra_code_count++] = strdup("}\n");
- }
- }
- else
- { //should never get here. Indexed texture rendering requires GLSL 1.30 or later
- // (for passing integers between vertex and fragment shaders)
- LL_ERRS() << "Indexed texture rendering requires GLSL 1.30 or later." << LL_ENDL;
- }
- }
-
- //copy file into memory
- enum {
- flag_write_to_out_of_extra_block_area = 0x01
- , flag_extra_block_marker_was_found = 0x02
- };
-
- unsigned char flags = flag_write_to_out_of_extra_block_area;
-
- GLuint out_of_extra_block_counter = 0, start_shader_code = shader_code_count, file_lines_count = 0;
-
-#define TOUCH_SHADERS 0
-
-#if TOUCH_SHADERS
- const char* marker = "// touched";
- bool touched = false;
-#endif
-
- while(NULL != fgets((char *)buff, 1024, file)
- && shader_code_count < (LL_ARRAY_SIZE(shader_code_text) - LL_ARRAY_SIZE(extra_code_text)))
- {
- file_lines_count++;
-
- bool extra_block_area_found = NULL != strstr((const char*)buff, "[EXTRA_CODE_HERE]");
-
-#if TOUCH_SHADERS
- if (NULL != strstr((const char*)buff, marker))
- {
- touched = true;
- }
-#endif
-
- if(extra_block_area_found && !(flag_extra_block_marker_was_found & flags))
- {
- if(!(flag_write_to_out_of_extra_block_area & flags))
- {
- //shift
- for(GLuint to = start_shader_code, from = extra_code_count + start_shader_code;
- from < shader_code_count; ++to, ++from)
- {
- shader_code_text[to] = shader_code_text[from];
- }
-
- shader_code_count -= extra_code_count;
- }
-
- //copy extra code
- for(GLuint n = 0; n < extra_code_count
- && shader_code_count < (LL_ARRAY_SIZE(shader_code_text) - LL_ARRAY_SIZE(extra_code_text)); ++n)
- {
- shader_code_text[shader_code_count++] = extra_code_text[n];
- }
-
- extra_code_count = 0;
-
- flags &= ~flag_write_to_out_of_extra_block_area;
- flags |= flag_extra_block_marker_was_found;
- }
- else
- {
- shader_code_text[shader_code_count] = (GLchar *)strdup((char *)buff);
-
- if(flag_write_to_out_of_extra_block_area & flags)
- {
- shader_code_text[extra_code_count + start_shader_code + out_of_extra_block_counter]
- = shader_code_text[shader_code_count];
- out_of_extra_block_counter++;
-
- if(out_of_extra_block_counter == extra_code_count)
- {
- shader_code_count += extra_code_count;
- flags &= ~flag_write_to_out_of_extra_block_area;
- }
- }
-
- ++shader_code_count;
- }
- } //while
-
- if(!(flag_extra_block_marker_was_found & flags))
- {
- for(GLuint n = start_shader_code; n < extra_code_count + start_shader_code; ++n)
- {
- shader_code_text[n] = extra_code_text[n - start_shader_code];
- }
-
- if (file_lines_count < extra_code_count)
- {
- shader_code_count += extra_code_count;
- }
-
- extra_code_count = 0;
- }
-
-#if TOUCH_SHADERS
- if (!touched)
- {
- fprintf(file, "\n%s\n", marker);
- }
-#endif
-
- fclose(file);
-
- //create shader object
- GLuint ret = glCreateShader(type);
-
- error = glGetError();
- if (error != GL_NO_ERROR)
- {
- LL_WARNS("ShaderLoading") << "GL ERROR in glCreateShader: " << error << " for file: " << open_file_name << LL_ENDL;
- if (ret)
- {
- glDeleteShader(ret); //no longer need handle
- ret = 0;
- }
- }
-
- //load source
- if (ret)
- {
- glShaderSource(ret, shader_code_count, (const GLchar**)shader_code_text, NULL);
-
- error = glGetError();
- if (error != GL_NO_ERROR)
- {
- LL_WARNS("ShaderLoading") << "GL ERROR in glShaderSource: " << error << " for file: " << open_file_name << LL_ENDL;
- glDeleteShader(ret); //no longer need handle
- ret = 0;
- }
- }
-
- //compile source
- if (ret)
- {
- glCompileShader(ret);
-
- error = glGetError();
- if (error != GL_NO_ERROR)
- {
- LL_WARNS("ShaderLoading") << "GL ERROR in glCompileShader: " << error << " for file: " << open_file_name << LL_ENDL;
- glDeleteShader(ret); //no longer need handle
- ret = 0;
- }
- }
-
- if (error == GL_NO_ERROR)
- {
- //check for errors
- GLint success = GL_TRUE;
- glGetShaderiv(ret, GL_COMPILE_STATUS, &success);
-
- error = glGetError();
- if (error != GL_NO_ERROR || success == GL_FALSE)
- {
- //an error occured, print log
- LL_WARNS("ShaderLoading") << "GLSL Compilation Error:" << LL_ENDL;
- dumpObjectLog(ret, true, open_file_name);
- dumpShaderSource(shader_code_count, shader_code_text);
- glDeleteShader(ret); //no longer need handle
- ret = 0;
- }
- }
- else
- {
- ret = 0;
- }
- stop_glerror();
-
- //free memory
- for (GLuint i = 0; i < shader_code_count; i++)
- {
- free(shader_code_text[i]);
- }
-
- //successfully loaded, save results
- if (ret)
- {
- // Add shader file to map
- if (type == GL_VERTEX_SHADER) {
- mVertexShaderObjects[filename] = ret;
- }
- else if (type == GL_FRAGMENT_SHADER) {
- mFragmentShaderObjects[filename] = ret;
- }
- shader_level = try_gpu_class;
- }
- else
- {
- if (shader_level > 1)
- {
- shader_level--;
- return loadShaderFile(filename, shader_level, type, defines, texture_index_channels);
- }
- LL_WARNS("ShaderLoading") << "Failed to load " << filename << LL_ENDL;
- }
- return ret;
-}
-
-bool LLShaderMgr::linkProgramObject(GLuint obj, bool suppress_errors)
-{
- //check for errors
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_SHADER("glLinkProgram");
- glLinkProgram(obj);
- }
-
- GLint success = GL_TRUE;
-
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_SHADER("glsl check link status");
- glGetProgramiv(obj, GL_LINK_STATUS, &success);
- if (!suppress_errors && success == GL_FALSE)
- {
- //an error occured, print log
- LL_SHADER_LOADING_WARNS() << "GLSL Linker Error:" << LL_ENDL;
- dumpObjectLog(obj, true, "linker");
- return success;
- }
- }
-
- std::string log = get_program_log(obj);
- LLStringUtil::toLower(log);
- if (log.find("software") != std::string::npos)
- {
- LL_SHADER_LOADING_WARNS() << "GLSL Linker: Running in Software:" << LL_ENDL;
- success = GL_FALSE;
- suppress_errors = false;
- }
- return success;
-}
-
-bool LLShaderMgr::validateProgramObject(GLuint obj)
-{
- //check program validity against current GL
- glValidateProgram(obj);
- GLint success = GL_TRUE;
- glGetProgramiv(obj, GL_LINK_STATUS, &success);
- if (success == GL_FALSE)
- {
- LL_SHADER_LOADING_WARNS() << "GLSL program not valid: " << LL_ENDL;
- dumpObjectLog(obj);
- }
- else
- {
- dumpObjectLog(obj, false);
- }
-
- return success;
-}
-
-void LLShaderMgr::initShaderCache(bool enabled, const LLUUID& old_cache_version, const LLUUID& current_cache_version)
-{
- LL_INFOS() << "Initializing shader cache" << LL_ENDL;
-
- mShaderCacheEnabled = gGLManager.mGLVersion >= 4.09 && enabled;
-
- if(!mShaderCacheEnabled || mShaderCacheInitialized)
- return;
-
- mShaderCacheInitialized = true;
-
- mShaderCacheDir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "shader_cache");
- LLFile::mkdir(mShaderCacheDir);
-
- {
- std::string meta_out_path = gDirUtilp->add(mShaderCacheDir, "shaderdata.llsd");
- if (gDirUtilp->fileExists(meta_out_path))
- {
- LL_INFOS() << "Loading shader cache metadata" << LL_ENDL;
-
- llifstream instream(meta_out_path);
- LLSD in_data;
- LLSDSerialize::fromNotation(in_data, instream, LLSDSerialize::SIZE_UNLIMITED);
- instream.close();
-
- if (old_cache_version == current_cache_version)
- {
- for (const auto& data_pair : llsd::inMap(in_data))
- {
- ProgramBinaryData binary_info = ProgramBinaryData();
- binary_info.mBinaryFormat = data_pair.second["binary_format"].asInteger();
- binary_info.mBinaryLength = data_pair.second["binary_size"].asInteger();
- binary_info.mLastUsedTime = data_pair.second["last_used"].asReal();
- mShaderBinaryCache.insert_or_assign(LLUUID(data_pair.first), binary_info);
- }
- }
- else
- {
- LL_INFOS() << "Shader cache version mismatch detected. Purging." << LL_ENDL;
- clearShaderCache();
- }
- }
- }
-}
-
-void LLShaderMgr::clearShaderCache()
-{
- std::string shader_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "shader_cache");
- LL_INFOS() << "Removing shader cache at " << shader_cache << LL_ENDL;
- const std::string mask = "*";
- gDirUtilp->deleteFilesInDir(shader_cache, mask);
- mShaderBinaryCache.clear();
-}
-
-void LLShaderMgr::persistShaderCacheMetadata()
-{
- if(!mShaderCacheEnabled) return;
-
- LL_INFOS() << "Persisting shader cache metadata to disk" << LL_ENDL;
-
- LLSD out = LLSD::emptyMap();
-
- static const F32 LRU_TIME = (60.f * 60.f) * 24.f * 7.f; // 14 days
- const F32 current_time = LLTimer::getTotalSeconds();
- for (auto it = mShaderBinaryCache.begin(); it != mShaderBinaryCache.end();)
- {
- const ProgramBinaryData& shader_metadata = it->second;
- if ((shader_metadata.mLastUsedTime + LRU_TIME) < current_time)
- {
- std::string shader_path = gDirUtilp->add(mShaderCacheDir, it->first.asString() + ".shaderbin");
- LLFile::remove(shader_path);
- it = mShaderBinaryCache.erase(it);
- }
- else
- {
- LLSD data = LLSD::emptyMap();
- data["binary_format"] = LLSD::Integer(shader_metadata.mBinaryFormat);
- data["binary_size"] = LLSD::Integer(shader_metadata.mBinaryLength);
- data["last_used"] = LLSD::Real(shader_metadata.mLastUsedTime);
- out[it->first.asString()] = data;
- ++it;
- }
- }
-
- std::string meta_out_path = gDirUtilp->add(mShaderCacheDir, "shaderdata.llsd");
- llofstream outstream(meta_out_path);
- LLSDSerialize::toNotation(out, outstream);
- outstream.close();
-}
-
-bool LLShaderMgr::loadCachedProgramBinary(LLGLSLShader* shader)
-{
- if (!mShaderCacheEnabled) return false;
-
- glProgramParameteri(shader->mProgramObject, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
-
- auto binary_iter = mShaderBinaryCache.find(shader->mShaderHash);
- if (binary_iter != mShaderBinaryCache.end())
- {
- std::string in_path = gDirUtilp->add(mShaderCacheDir, shader->mShaderHash.asString() + ".shaderbin");
- auto& shader_info = binary_iter->second;
- if (shader_info.mBinaryLength > 0)
- {
- std::vector<U8> in_data;
- in_data.resize(shader_info.mBinaryLength);
-
- LLUniqueFile filep = LLFile::fopen(in_path, "rb");
- if (filep)
- {
- size_t result = fread(in_data.data(), sizeof(U8), in_data.size(), filep);
- filep.close();
-
- if (result == in_data.size())
- {
- GLenum error = glGetError(); // Clear current error
- glProgramBinary(shader->mProgramObject, shader_info.mBinaryFormat, in_data.data(), shader_info.mBinaryLength);
-
- error = glGetError();
- GLint success = GL_TRUE;
- glGetProgramiv(shader->mProgramObject, GL_LINK_STATUS, &success);
- if (error == GL_NO_ERROR && success == GL_TRUE)
- {
- binary_iter->second.mLastUsedTime = LLTimer::getTotalSeconds();
- LL_INFOS() << "Loaded cached binary for shader: " << shader->mName << LL_ENDL;
- return true;
- }
- }
- }
- }
- //an error occured, normally we would print log but in this case it means the shader needs recompiling.
- LL_INFOS() << "Failed to load cached binary for shader: " << shader->mName << " falling back to compilation" << LL_ENDL;
- LLFile::remove(in_path);
- mShaderBinaryCache.erase(binary_iter);
- }
- return false;
-}
-
-bool LLShaderMgr::saveCachedProgramBinary(LLGLSLShader* shader)
-{
- if (!mShaderCacheEnabled) return true;
-
- ProgramBinaryData binary_info = ProgramBinaryData();
- glGetProgramiv(shader->mProgramObject, GL_PROGRAM_BINARY_LENGTH, &binary_info.mBinaryLength);
- if (binary_info.mBinaryLength > 0)
- {
- std::vector<U8> program_binary;
- program_binary.resize(binary_info.mBinaryLength);
-
- GLenum error = glGetError(); // Clear current error
- glGetProgramBinary(shader->mProgramObject, program_binary.size() * sizeof(U8), nullptr, &binary_info.mBinaryFormat, program_binary.data());
- error = glGetError();
- if (error == GL_NO_ERROR)
- {
- std::string out_path = gDirUtilp->add(mShaderCacheDir, shader->mShaderHash.asString() + ".shaderbin");
- LLUniqueFile outfile = LLFile::fopen(out_path, "wb");
- if (outfile)
- {
- fwrite(program_binary.data(), sizeof(U8), program_binary.size(), outfile);
- outfile.close();
-
- binary_info.mLastUsedTime = LLTimer::getTotalSeconds();
-
- mShaderBinaryCache.insert_or_assign(shader->mShaderHash, binary_info);
- return true;
- }
- }
- }
- return false;
-}
-
-//virtual
-void LLShaderMgr::initAttribsAndUniforms()
-{
- //MUST match order of enum in LLVertexBuffer.h
- mReservedAttribs.push_back("position");
- mReservedAttribs.push_back("normal");
- mReservedAttribs.push_back("texcoord0");
- mReservedAttribs.push_back("texcoord1");
- mReservedAttribs.push_back("texcoord2");
- mReservedAttribs.push_back("texcoord3");
- mReservedAttribs.push_back("diffuse_color");
- mReservedAttribs.push_back("emissive");
- mReservedAttribs.push_back("tangent");
- mReservedAttribs.push_back("weight");
- mReservedAttribs.push_back("weight4");
- mReservedAttribs.push_back("clothing");
- mReservedAttribs.push_back("texture_index");
-
- //matrix state
- mReservedUniforms.push_back("modelview_matrix");
- mReservedUniforms.push_back("projection_matrix");
- mReservedUniforms.push_back("inv_proj");
- mReservedUniforms.push_back("modelview_projection_matrix");
- mReservedUniforms.push_back("inv_modelview");
- mReservedUniforms.push_back("identity_matrix");
- mReservedUniforms.push_back("normal_matrix");
- mReservedUniforms.push_back("texture_matrix0");
- mReservedUniforms.push_back("texture_matrix1");
- mReservedUniforms.push_back("texture_matrix2");
- mReservedUniforms.push_back("texture_matrix3");
- mReservedUniforms.push_back("object_plane_s");
- mReservedUniforms.push_back("object_plane_t");
-
- mReservedUniforms.push_back("texture_base_color_transform"); // (GLTF)
- mReservedUniforms.push_back("texture_normal_transform"); // (GLTF)
- mReservedUniforms.push_back("texture_metallic_roughness_transform"); // (GLTF)
- mReservedUniforms.push_back("texture_emissive_transform"); // (GLTF)
-
- llassert(mReservedUniforms.size() == LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM+1);
-
- mReservedUniforms.push_back("viewport");
-
- mReservedUniforms.push_back("light_position");
- mReservedUniforms.push_back("light_direction");
- mReservedUniforms.push_back("light_attenuation");
- mReservedUniforms.push_back("light_deferred_attenuation");
- mReservedUniforms.push_back("light_diffuse");
- mReservedUniforms.push_back("light_ambient");
- mReservedUniforms.push_back("light_count");
- mReservedUniforms.push_back("light");
- mReservedUniforms.push_back("light_col");
- mReservedUniforms.push_back("far_z");
-
- llassert(mReservedUniforms.size() == LLShaderMgr::MULTI_LIGHT_FAR_Z+1);
-
- //NOTE: MUST match order in eGLSLReservedUniforms
- mReservedUniforms.push_back("proj_mat");
- mReservedUniforms.push_back("proj_near");
- mReservedUniforms.push_back("proj_p");
- mReservedUniforms.push_back("proj_n");
- mReservedUniforms.push_back("proj_origin");
- mReservedUniforms.push_back("proj_range");
- mReservedUniforms.push_back("proj_ambiance");
- mReservedUniforms.push_back("proj_shadow_idx");
- mReservedUniforms.push_back("shadow_fade");
- mReservedUniforms.push_back("proj_focus");
- mReservedUniforms.push_back("proj_lod");
- mReservedUniforms.push_back("proj_ambient_lod");
-
- llassert(mReservedUniforms.size() == LLShaderMgr::PROJECTOR_AMBIENT_LOD+1);
-
- mReservedUniforms.push_back("color");
- mReservedUniforms.push_back("emissiveColor");
- mReservedUniforms.push_back("metallicFactor");
- mReservedUniforms.push_back("roughnessFactor");
-
- mReservedUniforms.push_back("diffuseMap");
- mReservedUniforms.push_back("altDiffuseMap");
- mReservedUniforms.push_back("specularMap");
- mReservedUniforms.push_back("emissiveMap");
- mReservedUniforms.push_back("bumpMap");
- mReservedUniforms.push_back("bumpMap2");
- mReservedUniforms.push_back("environmentMap");
- mReservedUniforms.push_back("sceneMap");
- mReservedUniforms.push_back("sceneDepth");
- mReservedUniforms.push_back("reflectionProbes");
- mReservedUniforms.push_back("irradianceProbes");
- mReservedUniforms.push_back("cloud_noise_texture");
- mReservedUniforms.push_back("cloud_noise_texture_next");
- mReservedUniforms.push_back("fullbright");
- mReservedUniforms.push_back("lightnorm");
- mReservedUniforms.push_back("sunlight_color");
- mReservedUniforms.push_back("ambient_color");
- mReservedUniforms.push_back("sky_hdr_scale");
- mReservedUniforms.push_back("sky_sunlight_scale");
- mReservedUniforms.push_back("sky_ambient_scale");
- mReservedUniforms.push_back("blue_horizon");
- mReservedUniforms.push_back("blue_density");
- mReservedUniforms.push_back("haze_horizon");
- mReservedUniforms.push_back("haze_density");
- mReservedUniforms.push_back("cloud_shadow");
- mReservedUniforms.push_back("density_multiplier");
- mReservedUniforms.push_back("distance_multiplier");
- mReservedUniforms.push_back("max_y");
- mReservedUniforms.push_back("glow");
- mReservedUniforms.push_back("cloud_color");
- mReservedUniforms.push_back("cloud_pos_density1");
- mReservedUniforms.push_back("cloud_pos_density2");
- mReservedUniforms.push_back("cloud_scale");
- mReservedUniforms.push_back("gamma");
- mReservedUniforms.push_back("scene_light_strength");
-
- llassert(mReservedUniforms.size() == LLShaderMgr::SCENE_LIGHT_STRENGTH+1);
-
- mReservedUniforms.push_back("center");
- mReservedUniforms.push_back("size");
- mReservedUniforms.push_back("falloff");
-
- mReservedUniforms.push_back("box_center");
- mReservedUniforms.push_back("box_size");
-
-
- mReservedUniforms.push_back("minLuminance");
- mReservedUniforms.push_back("maxExtractAlpha");
- mReservedUniforms.push_back("lumWeights");
- mReservedUniforms.push_back("warmthWeights");
- mReservedUniforms.push_back("warmthAmount");
- mReservedUniforms.push_back("glowStrength");
- mReservedUniforms.push_back("glowDelta");
- mReservedUniforms.push_back("glowNoiseMap");
-
- llassert(mReservedUniforms.size() == LLShaderMgr::GLOW_NOISE_MAP+1);
-
-
- mReservedUniforms.push_back("minimum_alpha");
- mReservedUniforms.push_back("emissive_brightness");
-
- // Deferred
- mReservedUniforms.push_back("shadow_matrix");
- mReservedUniforms.push_back("env_mat");
- mReservedUniforms.push_back("shadow_clip");
- mReservedUniforms.push_back("sun_wash");
- mReservedUniforms.push_back("shadow_noise");
- mReservedUniforms.push_back("blur_size");
- mReservedUniforms.push_back("ssao_radius");
- mReservedUniforms.push_back("ssao_max_radius");
- mReservedUniforms.push_back("ssao_factor");
- mReservedUniforms.push_back("ssao_factor_inv");
- mReservedUniforms.push_back("ssao_effect_mat");
- mReservedUniforms.push_back("screen_res");
- mReservedUniforms.push_back("near_clip");
- mReservedUniforms.push_back("shadow_offset");
- mReservedUniforms.push_back("shadow_bias");
- mReservedUniforms.push_back("spot_shadow_bias");
- mReservedUniforms.push_back("spot_shadow_offset");
- mReservedUniforms.push_back("sun_dir");
- mReservedUniforms.push_back("moon_dir");
- mReservedUniforms.push_back("shadow_res");
- mReservedUniforms.push_back("proj_shadow_res");
- mReservedUniforms.push_back("depth_cutoff");
- mReservedUniforms.push_back("norm_cutoff");
- mReservedUniforms.push_back("shadow_target_width");
-
- llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH + 1);
-
- mReservedUniforms.push_back("iterationCount");
- mReservedUniforms.push_back("rayStep");
- mReservedUniforms.push_back("distanceBias");
- mReservedUniforms.push_back("depthRejectBias");
- mReservedUniforms.push_back("glossySampleCount");
- mReservedUniforms.push_back("noiseSine");
- mReservedUniforms.push_back("adaptiveStepMultiplier");
-
- mReservedUniforms.push_back("modelview_delta");
- mReservedUniforms.push_back("inv_modelview_delta");
- mReservedUniforms.push_back("cube_snapshot");
-
- mReservedUniforms.push_back("tc_scale");
- mReservedUniforms.push_back("rcp_screen_res");
- mReservedUniforms.push_back("rcp_frame_opt");
- mReservedUniforms.push_back("rcp_frame_opt2");
-
- mReservedUniforms.push_back("focal_distance");
- mReservedUniforms.push_back("blur_constant");
- mReservedUniforms.push_back("tan_pixel_angle");
- mReservedUniforms.push_back("magnification");
- mReservedUniforms.push_back("max_cof");
- mReservedUniforms.push_back("res_scale");
- mReservedUniforms.push_back("dof_width");
- mReservedUniforms.push_back("dof_height");
-
- mReservedUniforms.push_back("depthMap");
- mReservedUniforms.push_back("shadowMap0");
- mReservedUniforms.push_back("shadowMap1");
- mReservedUniforms.push_back("shadowMap2");
- mReservedUniforms.push_back("shadowMap3");
- mReservedUniforms.push_back("shadowMap4");
- mReservedUniforms.push_back("shadowMap5");
-
- llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_SHADOW5+1);
-
- mReservedUniforms.push_back("normalMap");
- mReservedUniforms.push_back("positionMap");
- mReservedUniforms.push_back("diffuseRect");
- mReservedUniforms.push_back("specularRect");
- mReservedUniforms.push_back("emissiveRect");
- mReservedUniforms.push_back("exposureMap");
- mReservedUniforms.push_back("brdfLut");
- mReservedUniforms.push_back("noiseMap");
- mReservedUniforms.push_back("lightFunc");
- mReservedUniforms.push_back("lightMap");
- mReservedUniforms.push_back("bloomMap");
- mReservedUniforms.push_back("projectionMap");
- mReservedUniforms.push_back("norm_mat");
- mReservedUniforms.push_back("texture_gamma");
-
- mReservedUniforms.push_back("specular_color");
- mReservedUniforms.push_back("env_intensity");
-
- mReservedUniforms.push_back("matrixPalette");
- mReservedUniforms.push_back("translationPalette");
-
- mReservedUniforms.push_back("screenTex");
- mReservedUniforms.push_back("screenDepth");
- mReservedUniforms.push_back("refTex");
- mReservedUniforms.push_back("eyeVec");
- mReservedUniforms.push_back("time");
- mReservedUniforms.push_back("waveDir1");
- mReservedUniforms.push_back("waveDir2");
- mReservedUniforms.push_back("lightDir");
- mReservedUniforms.push_back("specular");
- mReservedUniforms.push_back("lightExp");
- mReservedUniforms.push_back("waterFogColor");
- mReservedUniforms.push_back("waterFogColorLinear");
- mReservedUniforms.push_back("waterFogDensity");
- mReservedUniforms.push_back("waterFogKS");
- mReservedUniforms.push_back("refScale");
- mReservedUniforms.push_back("waterHeight");
- mReservedUniforms.push_back("waterPlane");
- mReservedUniforms.push_back("normScale");
- mReservedUniforms.push_back("fresnelScale");
- mReservedUniforms.push_back("fresnelOffset");
- mReservedUniforms.push_back("blurMultiplier");
- mReservedUniforms.push_back("sunAngle");
- mReservedUniforms.push_back("scaledAngle");
- mReservedUniforms.push_back("sunAngle2");
-
- mReservedUniforms.push_back("camPosLocal");
-
- mReservedUniforms.push_back("gWindDir");
- mReservedUniforms.push_back("gSinWaveParams");
- mReservedUniforms.push_back("gGravity");
-
- mReservedUniforms.push_back("detail_0");
- mReservedUniforms.push_back("detail_1");
- mReservedUniforms.push_back("detail_2");
- mReservedUniforms.push_back("detail_3");
- mReservedUniforms.push_back("alpha_ramp");
-
- mReservedUniforms.push_back("origin");
- mReservedUniforms.push_back("display_gamma");
-
- mReservedUniforms.push_back("inscatter");
- mReservedUniforms.push_back("sun_size");
- mReservedUniforms.push_back("fog_color");
-
- mReservedUniforms.push_back("transmittance_texture");
- mReservedUniforms.push_back("scattering_texture");
- mReservedUniforms.push_back("single_mie_scattering_texture");
- mReservedUniforms.push_back("irradiance_texture");
- mReservedUniforms.push_back("blend_factor");
- mReservedUniforms.push_back("moisture_level");
- mReservedUniforms.push_back("droplet_radius");
- mReservedUniforms.push_back("ice_level");
- mReservedUniforms.push_back("rainbow_map");
- mReservedUniforms.push_back("halo_map");
- mReservedUniforms.push_back("moon_brightness");
- mReservedUniforms.push_back("cloud_variance");
- mReservedUniforms.push_back("reflection_probe_ambiance");
- mReservedUniforms.push_back("max_probe_lod");
-
- mReservedUniforms.push_back("sh_input_r");
- mReservedUniforms.push_back("sh_input_g");
- mReservedUniforms.push_back("sh_input_b");
-
- mReservedUniforms.push_back("sun_moon_glow_factor");
- mReservedUniforms.push_back("water_edge");
- mReservedUniforms.push_back("sun_up_factor");
- mReservedUniforms.push_back("moonlight_color");
-
- llassert(mReservedUniforms.size() == END_RESERVED_UNIFORMS);
-
- std::set<std::string> dupe_check;
-
- for (U32 i = 0; i < mReservedUniforms.size(); ++i)
- {
- if (dupe_check.find(mReservedUniforms[i]) != dupe_check.end())
- {
- LL_ERRS() << "Duplicate reserved uniform name found: " << mReservedUniforms[i] << LL_ENDL;
- }
- dupe_check.insert(mReservedUniforms[i]);
- }
-}
-
+/**
+ * @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 "llrender.h"
+#include "llfile.h"
+#include "lldir.h"
+#include "llsdutil.h"
+#include "llsdserialize.h"
+#include "hbxxh.h"
+
+#if LL_DARWIN
+#include "OpenGL/OpenGL.h"
+#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;
+
+ if (features->attachNothing)
+ {
+ return true;
+ }
+ //////////////////////////////////////
+ // Attach Vertex Shader Features First
+ //////////////////////////////////////
+
+ // NOTE order of shader object attaching is VERY IMPORTANT!!!
+ if (features->calculatesAtmospherics)
+ {
+ if (!shader->attachVertexObject("windlight/atmosphericsVarsV.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (features->calculatesLighting || features->calculatesAtmospherics)
+ {
+ if (!shader->attachVertexObject("windlight/atmosphericsHelpersV.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (features->calculatesLighting)
+ {
+ if (features->isSpecular)
+ {
+ if (!shader->attachVertexObject("lighting/lightFuncSpecularV.glsl"))
+ {
+ return false;
+ }
+
+ if (!features->isAlphaLighting)
+ {
+ if (!shader->attachVertexObject("lighting/sumLightsSpecularV.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (!shader->attachVertexObject("lighting/lightSpecularV.glsl"))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!shader->attachVertexObject("lighting/lightFuncV.glsl"))
+ {
+ return false;
+ }
+
+ if (!features->isAlphaLighting)
+ {
+ if (!shader->attachVertexObject("lighting/sumLightsV.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (!shader->attachVertexObject("lighting/lightV.glsl"))
+ {
+ return false;
+ }
+ }
+ }
+
+ // NOTE order of shader object attaching is VERY IMPORTANT!!!
+ if (features->calculatesAtmospherics)
+ {
+ if (!shader->attachVertexObject("environment/srgbF.glsl")) // NOTE -- "F" suffix is superfluous here, there is nothing fragment specific in srgbF
+ {
+ return false;
+ }
+
+ if (!shader->attachVertexObject("windlight/atmosphericsFuncs.glsl")) {
+ return false;
+ }
+
+ if (!shader->attachVertexObject("windlight/atmosphericsV.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (features->hasSkinning)
+ {
+ if (!shader->attachVertexObject("avatar/avatarSkinV.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (features->hasObjectSkinning)
+ {
+ shader->mRiggedVariant = shader;
+ if (!shader->attachVertexObject("avatar/objectSkinV.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (!shader->attachVertexObject("deferred/textureUtilV.glsl"))
+ {
+ return false;
+ }
+
+ ///////////////////////////////////////
+ // Attach Fragment Shader Features Next
+ ///////////////////////////////////////
+
+// NOTE order of shader object attaching is VERY IMPORTANT!!!
+ if (features->hasSrgb || features->hasAtmospherics || features->calculatesAtmospherics || features->isDeferred)
+ {
+ if (!shader->attachFragmentObject("environment/srgbF.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if(features->calculatesAtmospherics || features->hasGamma || features->isDeferred)
+ {
+ if (!shader->attachFragmentObject("windlight/atmosphericsVarsF.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (features->calculatesLighting || features->calculatesAtmospherics)
+ {
+ if (!shader->attachFragmentObject("windlight/atmosphericsHelpersF.glsl"))
+ {
+ return false;
+ }
+ }
+
+ // we want this BEFORE shadows and AO because those facilities use pos/norm access
+ if (features->isDeferred || features->hasReflectionProbes)
+ {
+ if (!shader->attachFragmentObject("deferred/deferredUtil.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (features->hasScreenSpaceReflections || features->hasReflectionProbes)
+ {
+ if (!shader->attachFragmentObject("deferred/screenSpaceReflUtil.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (features->hasShadows)
+ {
+ if (!shader->attachFragmentObject("deferred/shadowUtil.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (features->hasReflectionProbes)
+ {
+ if (!shader->attachFragmentObject("deferred/reflectionProbeF.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (features->hasAmbientOcclusion)
+ {
+ if (!shader->attachFragmentObject("deferred/aoUtil.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (features->hasGamma || features->isDeferred)
+ {
+ if (!shader->attachFragmentObject("windlight/gammaF.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (features->encodesNormal)
+ {
+ if (!shader->attachFragmentObject("environment/encodeNormF.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (features->hasAtmospherics || features->isDeferred)
+ {
+ if (!shader->attachFragmentObject("windlight/atmosphericsFuncs.glsl")) {
+ return false;
+ }
+
+ if (!shader->attachFragmentObject("windlight/atmosphericsF.glsl"))
+ {
+ return false;
+ }
+ }
+
+ // NOTE order of shader object attaching is VERY IMPORTANT!!!
+ if (features->hasAtmospherics)
+ {
+ if (!shader->attachFragmentObject("environment/waterFogF.glsl"))
+ {
+ return false;
+ }
+ }
+
+ if (features->hasLighting)
+ {
+ if (features->disableTextureIndex)
+ {
+ if (features->hasAlphaMask)
+ {
+ if (!shader->attachFragmentObject("lighting/lightAlphaMaskNonIndexedF.glsl"))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!shader->attachFragmentObject("lighting/lightNonIndexedF.glsl"))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (features->hasAlphaMask)
+ {
+ if (!shader->attachFragmentObject("lighting/lightAlphaMaskF.glsl"))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!shader->attachFragmentObject("lighting/lightF.glsl"))
+ {
+ return false;
+ }
+ }
+ shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels-1, 1);
+ }
+ }
+
+ if (features->mIndexedTextureChannels <= 1)
+ {
+ if (!shader->attachVertexObject("objects/nonindexedTextureV.glsl"))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!shader->attachVertexObject("objects/indexedTextureV.glsl"))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//============================================================================
+// Load Shader
+
+static std::string get_shader_log(GLuint ret)
+{
+ std::string res;
+
+ //get log length
+ GLint length;
+ glGetShaderiv(ret, GL_INFO_LOG_LENGTH, &length);
+ if (length > 0)
+ {
+ //the log could be any size, so allocate appropriately
+ GLchar* log = new GLchar[length];
+ glGetShaderInfoLog(ret, length, &length, log);
+ res = std::string((char *)log);
+ delete[] log;
+ }
+ return res;
+}
+
+static std::string get_program_log(GLuint ret)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
+ std::string res;
+
+ //get log length
+ GLint length;
+ glGetProgramiv(ret, GL_INFO_LOG_LENGTH, &length);
+ if (length > 0)
+ {
+ //the log could be any size, so allocate appropriately
+ GLchar* log = new GLchar[length];
+ glGetProgramInfoLog(ret, length, &length, log);
+ res = std::string((char*)log);
+ delete[] log;
+ }
+ return res;
+}
+
+// get the info log for the given object, be it a shader or program object
+// NOTE: ret MUST be a shader OR a program object
+static std::string get_object_log(GLuint ret)
+{
+ if (glIsProgram(ret))
+ {
+ return get_program_log(ret);
+ }
+ else
+ {
+ llassert(glIsShader(ret));
+ return get_shader_log(ret);
+ }
+}
+
+//dump shader source for debugging
+void LLShaderMgr::dumpShaderSource(U32 shader_code_count, GLchar** shader_code_text)
+{
+ char num_str[16]; // U32 = max 10 digits
+
+ LL_SHADER_LOADING_WARNS() << "\n";
+
+ for (U32 i = 0; i < shader_code_count; i++)
+ {
+ snprintf(num_str, sizeof(num_str), "%4d: ", i+1);
+ std::string line_number(num_str);
+ LL_CONT << line_number << shader_code_text[i];
+ }
+ LL_CONT << LL_ENDL;
+}
+
+void LLShaderMgr::dumpObjectLog(GLuint ret, bool warns, const std::string& filename)
+{
+ std::string log;
+ log = get_object_log(ret);
+ std::string fname = filename;
+ if (filename.empty())
+ {
+ fname = "unknown shader file";
+ }
+
+ if (log.length() > 0)
+ {
+ LL_SHADER_LOADING_WARNS() << "Shader loading from " << fname << LL_ENDL;
+ LL_SHADER_LOADING_WARNS() << "\n" << log << LL_ENDL;
+ }
+ }
+
+GLuint LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type, std::map<std::string, std::string>* defines, S32 texture_index_channels)
+{
+
+// endsure work-around for missing GLSL funcs gets propogated to feature shader files (e.g. srgbF.glsl)
+#if LL_DARWIN
+ if (defines)
+ {
+ (*defines)["OLD_SELECT"] = "1";
+ }
+#endif
+
+ GLenum error = GL_NO_ERROR;
+
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ LL_SHADER_LOADING_WARNS() << "GL ERROR entering loadShaderFile(): " << error << " for file: " << filename << LL_ENDL;
+ }
+
+ if (filename.empty())
+ {
+ return 0;
+ }
+
+
+ //read in from file
+ LLFILE* file = NULL;
+
+ S32 try_gpu_class = shader_level;
+ S32 gpu_class;
+
+ std::string open_file_name;
+
+#if 0 // WIP -- try to come up with a way to fallback to an error shader without needing debug stubs all over the place in the shader tree
+ if (shader_level == -1)
+ {
+ // use "error" fallback
+ if (type == GL_VERTEX_SHADER)
+ {
+ open_file_name = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "shaders/errorV.glsl");
+ }
+ else
+ {
+ llassert(type == GL_FRAGMENT_SHADER); // type must be vertex or fragment shader
+ open_file_name = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "shaders/errorF.glsl");
+ }
+
+ file = LLFile::fopen(open_file_name, "r");
+ }
+ else
+#endif
+ {
+ //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;
+
+ open_file_name = fname.str();
+
+ /*
+ Would be awesome, if we didn't have shaders that re-use files
+ with different environments to say, add skinning, etc
+ can't depend on cached version to have evaluate ifdefs identically...
+ if we can define a deterministic hash for the shader based on
+ all the inputs, maybe we can save some time here.
+ if (mShaderObjects.count(filename) > 0)
+ {
+ return mShaderObjects[filename];
+ }
+
+ */
+
+ LL_DEBUGS("ShaderLoading") << "Looking in " << open_file_name << LL_ENDL;
+ file = LLFile::fopen(open_file_name, "r"); /* Flawfinder: ignore */
+ if (file)
+ {
+ LL_DEBUGS("ShaderLoading") << "Loading file: " << open_file_name << " (Want class " << gpu_class << ")" << LL_ENDL;
+ break; // done
+ }
+ }
+ }
+
+ if (file == NULL)
+ {
+ LL_WARNS("ShaderLoading") << "GLSL Shader file not found: " << open_file_name << LL_ENDL;
+ return 0;
+ }
+
+ //we can't have any lines longer than 1024 characters
+ //or any shaders longer than 4096 lines... deal - DaveP
+ GLchar buff[1024];
+ GLchar *extra_code_text[1024];
+ GLchar *shader_code_text[4096 + LL_ARRAY_SIZE(extra_code_text)] = { NULL };
+ GLuint extra_code_count = 0, shader_code_count = 0;
+ BOOST_STATIC_ASSERT(LL_ARRAY_SIZE(extra_code_text) < LL_ARRAY_SIZE(shader_code_text));
+
+
+ S32 major_version = gGLManager.mGLSLVersionMajor;
+ S32 minor_version = gGLManager.mGLSLVersionMinor;
+
+ if (major_version == 1 && minor_version < 30)
+ {
+ llassert(false); // GL 3.1 or later required
+ }
+ else
+ {
+ if (major_version >= 4)
+ {
+ //set version to 400 or 420
+ if (minor_version >= 20)
+ {
+ shader_code_text[shader_code_count++] = strdup("#version 420\n");
+ }
+ else
+ {
+ shader_code_text[shader_code_count++] = strdup("#version 400\n");
+ }
+ }
+ else if (major_version == 3)
+ {
+ if (minor_version < 10)
+ {
+ shader_code_text[shader_code_count++] = strdup("#version 300\n");
+ }
+ else if (minor_version <= 19)
+ {
+ shader_code_text[shader_code_count++] = strdup("#version 310\n");
+ }
+ else if (minor_version <= 29)
+ {
+ shader_code_text[shader_code_count++] = strdup("#version 320\n");
+ }
+ else
+ {
+ shader_code_text[shader_code_count++] = strdup("#version 330\n");
+ }
+ }
+ else
+ {
+ //set version to 1.40
+ shader_code_text[shader_code_count++] = strdup("#version 140\n");
+ //some implementations of GLSL 1.30 require integer precision be explicitly declared
+ extra_code_text[extra_code_count++] = strdup("precision mediump int;\n");
+ extra_code_text[extra_code_count++] = strdup("precision highp float;\n");
+ }
+
+ extra_code_text[extra_code_count++] = strdup("#define FXAA_GLSL_130 1\n");
+ }
+
+ // Use alpha float to store bit flags
+ // See: C++: addDeferredAttachment(), shader: frag_data[2]
+ extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_SKIP_ATMOS 0.0 \n"); // atmo kill
+ extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_ATMOS 0.34\n"); // bit 0
+ extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_PBR 0.67\n"); // bit 1
+ extra_code_text[extra_code_count++] = strdup("#define GET_GBUFFER_FLAG(flag) (abs(norm.w-flag)< 0.1)\n");
+
+ if (defines)
+ {
+ for (auto iter = defines->begin(); iter != defines->end(); ++iter)
+ {
+ std::string define = "#define " + iter->first + " " + iter->second + "\n";
+ extra_code_text[extra_code_count++] = (GLchar *) strdup(define.c_str());
+ }
+ }
+
+ if( gGLManager.mIsAMD )
+ {
+ extra_code_text[extra_code_count++] = strdup( "#define IS_AMD_CARD 1\n" );
+ }
+
+ if (texture_index_channels > 0 && type == GL_FRAGMENT_SHADER)
+ {
+ //use specified number of texture channels for indexed texture rendering
+
+ /* prepend shader code that looks like this:
+
+ uniform sampler2D tex0;
+ uniform sampler2D tex1;
+ uniform sampler2D tex2;
+ .
+ .
+ .
+ uniform sampler2D texN;
+
+ flat in int vary_texture_index;
+
+ vec4 ret = vec4(1,0,1,1);
+
+ vec4 diffuseLookup(vec2 texcoord)
+ {
+ switch (vary_texture_index)
+ {
+ case 0: ret = texture(tex0, texcoord); break;
+ case 1: ret = texture(tex1, texcoord); break;
+ case 2: ret = texture(tex2, texcoord); break;
+ .
+ .
+ .
+ case N: return texture(texN, texcoord); break;
+ }
+
+ return ret;
+ }
+ */
+
+ extra_code_text[extra_code_count++] = strdup("#define HAS_DIFFUSE_LOOKUP\n");
+
+ //uniform declartion
+ for (S32 i = 0; i < texture_index_channels; ++i)
+ {
+ std::string decl = llformat("uniform sampler2D tex%d;\n", i);
+ extra_code_text[extra_code_count++] = strdup(decl.c_str());
+ }
+
+ if (texture_index_channels > 1)
+ {
+ extra_code_text[extra_code_count++] = strdup("flat in int vary_texture_index;\n");
+ }
+
+ extra_code_text[extra_code_count++] = strdup("vec4 diffuseLookup(vec2 texcoord)\n");
+ extra_code_text[extra_code_count++] = strdup("{\n");
+
+
+ if (texture_index_channels == 1)
+ { //don't use flow control, that's silly
+ extra_code_text[extra_code_count++] = strdup("return texture(tex0, texcoord);\n");
+ extra_code_text[extra_code_count++] = strdup("}\n");
+ }
+ else if (major_version > 1 || minor_version >= 30)
+ { //switches are supported in GLSL 1.30 and later
+ if (gGLManager.mIsNVIDIA)
+ { //switches are unreliable on some NVIDIA drivers
+ for (U32 i = 0; i < texture_index_channels; ++i)
+ {
+ std::string if_string = llformat("\t%sif (vary_texture_index == %d) { return texture(tex%d, texcoord); }\n", i > 0 ? "else " : "", i, i);
+ extra_code_text[extra_code_count++] = strdup(if_string.c_str());
+ }
+ extra_code_text[extra_code_count++] = strdup("\treturn vec4(1,0,1,1);\n");
+ extra_code_text[extra_code_count++] = strdup("}\n");
+ }
+ else
+ {
+ extra_code_text[extra_code_count++] = strdup("\tvec4 ret = vec4(1,0,1,1);\n");
+ extra_code_text[extra_code_count++] = strdup("\tswitch (vary_texture_index)\n");
+ extra_code_text[extra_code_count++] = strdup("\t{\n");
+
+ //switch body
+ for (S32 i = 0; i < texture_index_channels; ++i)
+ {
+ std::string case_str = llformat("\t\tcase %d: return texture(tex%d, texcoord);\n", i, i);
+ extra_code_text[extra_code_count++] = strdup(case_str.c_str());
+ }
+
+ extra_code_text[extra_code_count++] = strdup("\t}\n");
+ extra_code_text[extra_code_count++] = strdup("\treturn ret;\n");
+ extra_code_text[extra_code_count++] = strdup("}\n");
+ }
+ }
+ else
+ { //should never get here. Indexed texture rendering requires GLSL 1.30 or later
+ // (for passing integers between vertex and fragment shaders)
+ LL_ERRS() << "Indexed texture rendering requires GLSL 1.30 or later." << LL_ENDL;
+ }
+ }
+
+ //copy file into memory
+ enum {
+ flag_write_to_out_of_extra_block_area = 0x01
+ , flag_extra_block_marker_was_found = 0x02
+ };
+
+ unsigned char flags = flag_write_to_out_of_extra_block_area;
+
+ GLuint out_of_extra_block_counter = 0, start_shader_code = shader_code_count, file_lines_count = 0;
+
+#define TOUCH_SHADERS 0
+
+#if TOUCH_SHADERS
+ const char* marker = "// touched";
+ bool touched = false;
+#endif
+
+ while(NULL != fgets((char *)buff, 1024, file)
+ && shader_code_count < (LL_ARRAY_SIZE(shader_code_text) - LL_ARRAY_SIZE(extra_code_text)))
+ {
+ file_lines_count++;
+
+ bool extra_block_area_found = NULL != strstr((const char*)buff, "[EXTRA_CODE_HERE]");
+
+#if TOUCH_SHADERS
+ if (NULL != strstr((const char*)buff, marker))
+ {
+ touched = true;
+ }
+#endif
+
+ if(extra_block_area_found && !(flag_extra_block_marker_was_found & flags))
+ {
+ if(!(flag_write_to_out_of_extra_block_area & flags))
+ {
+ //shift
+ for(GLuint to = start_shader_code, from = extra_code_count + start_shader_code;
+ from < shader_code_count; ++to, ++from)
+ {
+ shader_code_text[to] = shader_code_text[from];
+ }
+
+ shader_code_count -= extra_code_count;
+ }
+
+ //copy extra code
+ for(GLuint n = 0; n < extra_code_count
+ && shader_code_count < (LL_ARRAY_SIZE(shader_code_text) - LL_ARRAY_SIZE(extra_code_text)); ++n)
+ {
+ shader_code_text[shader_code_count++] = extra_code_text[n];
+ }
+
+ extra_code_count = 0;
+
+ flags &= ~flag_write_to_out_of_extra_block_area;
+ flags |= flag_extra_block_marker_was_found;
+ }
+ else
+ {
+ shader_code_text[shader_code_count] = (GLchar *)strdup((char *)buff);
+
+ if(flag_write_to_out_of_extra_block_area & flags)
+ {
+ shader_code_text[extra_code_count + start_shader_code + out_of_extra_block_counter]
+ = shader_code_text[shader_code_count];
+ out_of_extra_block_counter++;
+
+ if(out_of_extra_block_counter == extra_code_count)
+ {
+ shader_code_count += extra_code_count;
+ flags &= ~flag_write_to_out_of_extra_block_area;
+ }
+ }
+
+ ++shader_code_count;
+ }
+ } //while
+
+ if(!(flag_extra_block_marker_was_found & flags))
+ {
+ for(GLuint n = start_shader_code; n < extra_code_count + start_shader_code; ++n)
+ {
+ shader_code_text[n] = extra_code_text[n - start_shader_code];
+ }
+
+ if (file_lines_count < extra_code_count)
+ {
+ shader_code_count += extra_code_count;
+ }
+
+ extra_code_count = 0;
+ }
+
+#if TOUCH_SHADERS
+ if (!touched)
+ {
+ fprintf(file, "\n%s\n", marker);
+ }
+#endif
+
+ fclose(file);
+
+ //create shader object
+ GLuint ret = glCreateShader(type);
+
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ LL_WARNS("ShaderLoading") << "GL ERROR in glCreateShader: " << error << " for file: " << open_file_name << LL_ENDL;
+ if (ret)
+ {
+ glDeleteShader(ret); //no longer need handle
+ ret = 0;
+ }
+ }
+
+ //load source
+ if (ret)
+ {
+ glShaderSource(ret, shader_code_count, (const GLchar**)shader_code_text, NULL);
+
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ LL_WARNS("ShaderLoading") << "GL ERROR in glShaderSource: " << error << " for file: " << open_file_name << LL_ENDL;
+ glDeleteShader(ret); //no longer need handle
+ ret = 0;
+ }
+ }
+
+ //compile source
+ if (ret)
+ {
+ glCompileShader(ret);
+
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ LL_WARNS("ShaderLoading") << "GL ERROR in glCompileShader: " << error << " for file: " << open_file_name << LL_ENDL;
+ glDeleteShader(ret); //no longer need handle
+ ret = 0;
+ }
+ }
+
+ if (error == GL_NO_ERROR)
+ {
+ //check for errors
+ GLint success = GL_TRUE;
+ glGetShaderiv(ret, GL_COMPILE_STATUS, &success);
+
+ error = glGetError();
+ if (error != GL_NO_ERROR || success == GL_FALSE)
+ {
+ //an error occured, print log
+ LL_WARNS("ShaderLoading") << "GLSL Compilation Error:" << LL_ENDL;
+ dumpObjectLog(ret, true, open_file_name);
+ dumpShaderSource(shader_code_count, shader_code_text);
+ glDeleteShader(ret); //no longer need handle
+ ret = 0;
+ }
+ }
+ else
+ {
+ ret = 0;
+ }
+ stop_glerror();
+
+ //free memory
+ for (GLuint i = 0; i < shader_code_count; i++)
+ {
+ free(shader_code_text[i]);
+ }
+
+ //successfully loaded, save results
+ if (ret)
+ {
+ // Add shader file to map
+ if (type == GL_VERTEX_SHADER) {
+ mVertexShaderObjects[filename] = ret;
+ }
+ else if (type == GL_FRAGMENT_SHADER) {
+ mFragmentShaderObjects[filename] = ret;
+ }
+ shader_level = try_gpu_class;
+ }
+ else
+ {
+ if (shader_level > 1)
+ {
+ shader_level--;
+ return loadShaderFile(filename, shader_level, type, defines, texture_index_channels);
+ }
+ LL_WARNS("ShaderLoading") << "Failed to load " << filename << LL_ENDL;
+ }
+ return ret;
+}
+
+bool LLShaderMgr::linkProgramObject(GLuint obj, bool suppress_errors)
+{
+ //check for errors
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_SHADER("glLinkProgram");
+ glLinkProgram(obj);
+ }
+
+ GLint success = GL_TRUE;
+
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_SHADER("glsl check link status");
+ glGetProgramiv(obj, GL_LINK_STATUS, &success);
+ if (!suppress_errors && success == GL_FALSE)
+ {
+ //an error occured, print log
+ LL_SHADER_LOADING_WARNS() << "GLSL Linker Error:" << LL_ENDL;
+ dumpObjectLog(obj, true, "linker");
+ return success;
+ }
+ }
+
+ std::string log = get_program_log(obj);
+ LLStringUtil::toLower(log);
+ if (log.find("software") != std::string::npos)
+ {
+ LL_SHADER_LOADING_WARNS() << "GLSL Linker: Running in Software:" << LL_ENDL;
+ success = GL_FALSE;
+ suppress_errors = false;
+ }
+ return success;
+}
+
+bool LLShaderMgr::validateProgramObject(GLuint obj)
+{
+ //check program validity against current GL
+ glValidateProgram(obj);
+ GLint success = GL_TRUE;
+ glGetProgramiv(obj, GL_LINK_STATUS, &success);
+ if (success == GL_FALSE)
+ {
+ LL_SHADER_LOADING_WARNS() << "GLSL program not valid: " << LL_ENDL;
+ dumpObjectLog(obj);
+ }
+ else
+ {
+ dumpObjectLog(obj, false);
+ }
+
+ return success;
+}
+
+void LLShaderMgr::initShaderCache(bool enabled, const LLUUID& old_cache_version, const LLUUID& current_cache_version)
+{
+ LL_INFOS() << "Initializing shader cache" << LL_ENDL;
+
+ mShaderCacheEnabled = gGLManager.mGLVersion >= 4.09 && enabled;
+
+ if(!mShaderCacheEnabled || mShaderCacheInitialized)
+ return;
+
+ mShaderCacheInitialized = true;
+
+ mShaderCacheDir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "shader_cache");
+ LLFile::mkdir(mShaderCacheDir);
+
+ {
+ std::string meta_out_path = gDirUtilp->add(mShaderCacheDir, "shaderdata.llsd");
+ if (gDirUtilp->fileExists(meta_out_path))
+ {
+ LL_INFOS() << "Loading shader cache metadata" << LL_ENDL;
+
+ llifstream instream(meta_out_path);
+ LLSD in_data;
+ LLSDSerialize::fromNotation(in_data, instream, LLSDSerialize::SIZE_UNLIMITED);
+ instream.close();
+
+ if (old_cache_version == current_cache_version)
+ {
+ for (const auto& data_pair : llsd::inMap(in_data))
+ {
+ ProgramBinaryData binary_info = ProgramBinaryData();
+ binary_info.mBinaryFormat = data_pair.second["binary_format"].asInteger();
+ binary_info.mBinaryLength = data_pair.second["binary_size"].asInteger();
+ binary_info.mLastUsedTime = data_pair.second["last_used"].asReal();
+ mShaderBinaryCache.insert_or_assign(LLUUID(data_pair.first), binary_info);
+ }
+ }
+ else
+ {
+ LL_INFOS() << "Shader cache version mismatch detected. Purging." << LL_ENDL;
+ clearShaderCache();
+ }
+ }
+ }
+}
+
+void LLShaderMgr::clearShaderCache()
+{
+ std::string shader_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "shader_cache");
+ LL_INFOS() << "Removing shader cache at " << shader_cache << LL_ENDL;
+ const std::string mask = "*";
+ gDirUtilp->deleteFilesInDir(shader_cache, mask);
+ mShaderBinaryCache.clear();
+}
+
+void LLShaderMgr::persistShaderCacheMetadata()
+{
+ if(!mShaderCacheEnabled) return;
+
+ LL_INFOS() << "Persisting shader cache metadata to disk" << LL_ENDL;
+
+ LLSD out = LLSD::emptyMap();
+
+ static const F32 LRU_TIME = (60.f * 60.f) * 24.f * 7.f; // 14 days
+ const F32 current_time = LLTimer::getTotalSeconds();
+ for (auto it = mShaderBinaryCache.begin(); it != mShaderBinaryCache.end();)
+ {
+ const ProgramBinaryData& shader_metadata = it->second;
+ if ((shader_metadata.mLastUsedTime + LRU_TIME) < current_time)
+ {
+ std::string shader_path = gDirUtilp->add(mShaderCacheDir, it->first.asString() + ".shaderbin");
+ LLFile::remove(shader_path);
+ it = mShaderBinaryCache.erase(it);
+ }
+ else
+ {
+ LLSD data = LLSD::emptyMap();
+ data["binary_format"] = LLSD::Integer(shader_metadata.mBinaryFormat);
+ data["binary_size"] = LLSD::Integer(shader_metadata.mBinaryLength);
+ data["last_used"] = LLSD::Real(shader_metadata.mLastUsedTime);
+ out[it->first.asString()] = data;
+ ++it;
+ }
+ }
+
+ std::string meta_out_path = gDirUtilp->add(mShaderCacheDir, "shaderdata.llsd");
+ llofstream outstream(meta_out_path);
+ LLSDSerialize::toNotation(out, outstream);
+ outstream.close();
+}
+
+bool LLShaderMgr::loadCachedProgramBinary(LLGLSLShader* shader)
+{
+ if (!mShaderCacheEnabled) return false;
+
+ glProgramParameteri(shader->mProgramObject, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
+
+ auto binary_iter = mShaderBinaryCache.find(shader->mShaderHash);
+ if (binary_iter != mShaderBinaryCache.end())
+ {
+ std::string in_path = gDirUtilp->add(mShaderCacheDir, shader->mShaderHash.asString() + ".shaderbin");
+ auto& shader_info = binary_iter->second;
+ if (shader_info.mBinaryLength > 0)
+ {
+ std::vector<U8> in_data;
+ in_data.resize(shader_info.mBinaryLength);
+
+ LLUniqueFile filep = LLFile::fopen(in_path, "rb");
+ if (filep)
+ {
+ size_t result = fread(in_data.data(), sizeof(U8), in_data.size(), filep);
+ filep.close();
+
+ if (result == in_data.size())
+ {
+ GLenum error = glGetError(); // Clear current error
+ glProgramBinary(shader->mProgramObject, shader_info.mBinaryFormat, in_data.data(), shader_info.mBinaryLength);
+
+ error = glGetError();
+ GLint success = GL_TRUE;
+ glGetProgramiv(shader->mProgramObject, GL_LINK_STATUS, &success);
+ if (error == GL_NO_ERROR && success == GL_TRUE)
+ {
+ binary_iter->second.mLastUsedTime = LLTimer::getTotalSeconds();
+ LL_INFOS() << "Loaded cached binary for shader: " << shader->mName << LL_ENDL;
+ return true;
+ }
+ }
+ }
+ }
+ //an error occured, normally we would print log but in this case it means the shader needs recompiling.
+ LL_INFOS() << "Failed to load cached binary for shader: " << shader->mName << " falling back to compilation" << LL_ENDL;
+ LLFile::remove(in_path);
+ mShaderBinaryCache.erase(binary_iter);
+ }
+ return false;
+}
+
+bool LLShaderMgr::saveCachedProgramBinary(LLGLSLShader* shader)
+{
+ if (!mShaderCacheEnabled) return true;
+
+ ProgramBinaryData binary_info = ProgramBinaryData();
+ glGetProgramiv(shader->mProgramObject, GL_PROGRAM_BINARY_LENGTH, &binary_info.mBinaryLength);
+ if (binary_info.mBinaryLength > 0)
+ {
+ std::vector<U8> program_binary;
+ program_binary.resize(binary_info.mBinaryLength);
+
+ GLenum error = glGetError(); // Clear current error
+ glGetProgramBinary(shader->mProgramObject, program_binary.size() * sizeof(U8), nullptr, &binary_info.mBinaryFormat, program_binary.data());
+ error = glGetError();
+ if (error == GL_NO_ERROR)
+ {
+ std::string out_path = gDirUtilp->add(mShaderCacheDir, shader->mShaderHash.asString() + ".shaderbin");
+ LLUniqueFile outfile = LLFile::fopen(out_path, "wb");
+ if (outfile)
+ {
+ fwrite(program_binary.data(), sizeof(U8), program_binary.size(), outfile);
+ outfile.close();
+
+ binary_info.mLastUsedTime = LLTimer::getTotalSeconds();
+
+ mShaderBinaryCache.insert_or_assign(shader->mShaderHash, binary_info);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//virtual
+void LLShaderMgr::initAttribsAndUniforms()
+{
+ //MUST match order of enum in LLVertexBuffer.h
+ mReservedAttribs.push_back("position");
+ mReservedAttribs.push_back("normal");
+ mReservedAttribs.push_back("texcoord0");
+ mReservedAttribs.push_back("texcoord1");
+ mReservedAttribs.push_back("texcoord2");
+ mReservedAttribs.push_back("texcoord3");
+ mReservedAttribs.push_back("diffuse_color");
+ mReservedAttribs.push_back("emissive");
+ mReservedAttribs.push_back("tangent");
+ mReservedAttribs.push_back("weight");
+ mReservedAttribs.push_back("weight4");
+ mReservedAttribs.push_back("clothing");
+ mReservedAttribs.push_back("texture_index");
+
+ //matrix state
+ mReservedUniforms.push_back("modelview_matrix");
+ mReservedUniforms.push_back("projection_matrix");
+ mReservedUniforms.push_back("inv_proj");
+ mReservedUniforms.push_back("modelview_projection_matrix");
+ mReservedUniforms.push_back("inv_modelview");
+ mReservedUniforms.push_back("identity_matrix");
+ mReservedUniforms.push_back("normal_matrix");
+ mReservedUniforms.push_back("texture_matrix0");
+ mReservedUniforms.push_back("texture_matrix1");
+ mReservedUniforms.push_back("texture_matrix2");
+ mReservedUniforms.push_back("texture_matrix3");
+ mReservedUniforms.push_back("object_plane_s");
+ mReservedUniforms.push_back("object_plane_t");
+
+ mReservedUniforms.push_back("texture_base_color_transform"); // (GLTF)
+ mReservedUniforms.push_back("texture_normal_transform"); // (GLTF)
+ mReservedUniforms.push_back("texture_metallic_roughness_transform"); // (GLTF)
+ mReservedUniforms.push_back("texture_emissive_transform"); // (GLTF)
+
+ llassert(mReservedUniforms.size() == LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM+1);
+
+ mReservedUniforms.push_back("viewport");
+
+ mReservedUniforms.push_back("light_position");
+ mReservedUniforms.push_back("light_direction");
+ mReservedUniforms.push_back("light_attenuation");
+ mReservedUniforms.push_back("light_deferred_attenuation");
+ mReservedUniforms.push_back("light_diffuse");
+ mReservedUniforms.push_back("light_ambient");
+ mReservedUniforms.push_back("light_count");
+ mReservedUniforms.push_back("light");
+ mReservedUniforms.push_back("light_col");
+ mReservedUniforms.push_back("far_z");
+
+ llassert(mReservedUniforms.size() == LLShaderMgr::MULTI_LIGHT_FAR_Z+1);
+
+ //NOTE: MUST match order in eGLSLReservedUniforms
+ mReservedUniforms.push_back("proj_mat");
+ mReservedUniforms.push_back("proj_near");
+ mReservedUniforms.push_back("proj_p");
+ mReservedUniforms.push_back("proj_n");
+ mReservedUniforms.push_back("proj_origin");
+ mReservedUniforms.push_back("proj_range");
+ mReservedUniforms.push_back("proj_ambiance");
+ mReservedUniforms.push_back("proj_shadow_idx");
+ mReservedUniforms.push_back("shadow_fade");
+ mReservedUniforms.push_back("proj_focus");
+ mReservedUniforms.push_back("proj_lod");
+ mReservedUniforms.push_back("proj_ambient_lod");
+
+ llassert(mReservedUniforms.size() == LLShaderMgr::PROJECTOR_AMBIENT_LOD+1);
+
+ mReservedUniforms.push_back("color");
+ mReservedUniforms.push_back("emissiveColor");
+ mReservedUniforms.push_back("metallicFactor");
+ mReservedUniforms.push_back("roughnessFactor");
+
+ mReservedUniforms.push_back("diffuseMap");
+ mReservedUniforms.push_back("altDiffuseMap");
+ mReservedUniforms.push_back("specularMap");
+ mReservedUniforms.push_back("emissiveMap");
+ mReservedUniforms.push_back("bumpMap");
+ mReservedUniforms.push_back("bumpMap2");
+ mReservedUniforms.push_back("environmentMap");
+ mReservedUniforms.push_back("sceneMap");
+ mReservedUniforms.push_back("sceneDepth");
+ mReservedUniforms.push_back("reflectionProbes");
+ mReservedUniforms.push_back("irradianceProbes");
+ mReservedUniforms.push_back("cloud_noise_texture");
+ mReservedUniforms.push_back("cloud_noise_texture_next");
+ mReservedUniforms.push_back("fullbright");
+ mReservedUniforms.push_back("lightnorm");
+ mReservedUniforms.push_back("sunlight_color");
+ mReservedUniforms.push_back("ambient_color");
+ mReservedUniforms.push_back("sky_hdr_scale");
+ mReservedUniforms.push_back("sky_sunlight_scale");
+ mReservedUniforms.push_back("sky_ambient_scale");
+ mReservedUniforms.push_back("blue_horizon");
+ mReservedUniforms.push_back("blue_density");
+ mReservedUniforms.push_back("haze_horizon");
+ mReservedUniforms.push_back("haze_density");
+ mReservedUniforms.push_back("cloud_shadow");
+ mReservedUniforms.push_back("density_multiplier");
+ mReservedUniforms.push_back("distance_multiplier");
+ mReservedUniforms.push_back("max_y");
+ mReservedUniforms.push_back("glow");
+ mReservedUniforms.push_back("cloud_color");
+ mReservedUniforms.push_back("cloud_pos_density1");
+ mReservedUniforms.push_back("cloud_pos_density2");
+ mReservedUniforms.push_back("cloud_scale");
+ mReservedUniforms.push_back("gamma");
+ mReservedUniforms.push_back("scene_light_strength");
+
+ llassert(mReservedUniforms.size() == LLShaderMgr::SCENE_LIGHT_STRENGTH+1);
+
+ mReservedUniforms.push_back("center");
+ mReservedUniforms.push_back("size");
+ mReservedUniforms.push_back("falloff");
+
+ mReservedUniforms.push_back("box_center");
+ mReservedUniforms.push_back("box_size");
+
+
+ mReservedUniforms.push_back("minLuminance");
+ mReservedUniforms.push_back("maxExtractAlpha");
+ mReservedUniforms.push_back("lumWeights");
+ mReservedUniforms.push_back("warmthWeights");
+ mReservedUniforms.push_back("warmthAmount");
+ mReservedUniforms.push_back("glowStrength");
+ mReservedUniforms.push_back("glowDelta");
+ mReservedUniforms.push_back("glowNoiseMap");
+
+ llassert(mReservedUniforms.size() == LLShaderMgr::GLOW_NOISE_MAP+1);
+
+
+ mReservedUniforms.push_back("minimum_alpha");
+ mReservedUniforms.push_back("emissive_brightness");
+
+ // Deferred
+ mReservedUniforms.push_back("shadow_matrix");
+ mReservedUniforms.push_back("env_mat");
+ mReservedUniforms.push_back("shadow_clip");
+ mReservedUniforms.push_back("sun_wash");
+ mReservedUniforms.push_back("shadow_noise");
+ mReservedUniforms.push_back("blur_size");
+ mReservedUniforms.push_back("ssao_radius");
+ mReservedUniforms.push_back("ssao_max_radius");
+ mReservedUniforms.push_back("ssao_factor");
+ mReservedUniforms.push_back("ssao_factor_inv");
+ mReservedUniforms.push_back("ssao_effect_mat");
+ mReservedUniforms.push_back("screen_res");
+ mReservedUniforms.push_back("near_clip");
+ mReservedUniforms.push_back("shadow_offset");
+ mReservedUniforms.push_back("shadow_bias");
+ mReservedUniforms.push_back("spot_shadow_bias");
+ mReservedUniforms.push_back("spot_shadow_offset");
+ mReservedUniforms.push_back("sun_dir");
+ mReservedUniforms.push_back("moon_dir");
+ mReservedUniforms.push_back("shadow_res");
+ mReservedUniforms.push_back("proj_shadow_res");
+ mReservedUniforms.push_back("depth_cutoff");
+ mReservedUniforms.push_back("norm_cutoff");
+ mReservedUniforms.push_back("shadow_target_width");
+
+ llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH + 1);
+
+ mReservedUniforms.push_back("iterationCount");
+ mReservedUniforms.push_back("rayStep");
+ mReservedUniforms.push_back("distanceBias");
+ mReservedUniforms.push_back("depthRejectBias");
+ mReservedUniforms.push_back("glossySampleCount");
+ mReservedUniforms.push_back("noiseSine");
+ mReservedUniforms.push_back("adaptiveStepMultiplier");
+
+ mReservedUniforms.push_back("modelview_delta");
+ mReservedUniforms.push_back("inv_modelview_delta");
+ mReservedUniforms.push_back("cube_snapshot");
+
+ mReservedUniforms.push_back("tc_scale");
+ mReservedUniforms.push_back("rcp_screen_res");
+ mReservedUniforms.push_back("rcp_frame_opt");
+ mReservedUniforms.push_back("rcp_frame_opt2");
+
+ mReservedUniforms.push_back("focal_distance");
+ mReservedUniforms.push_back("blur_constant");
+ mReservedUniforms.push_back("tan_pixel_angle");
+ mReservedUniforms.push_back("magnification");
+ mReservedUniforms.push_back("max_cof");
+ mReservedUniforms.push_back("res_scale");
+ mReservedUniforms.push_back("dof_width");
+ mReservedUniforms.push_back("dof_height");
+
+ mReservedUniforms.push_back("depthMap");
+ mReservedUniforms.push_back("shadowMap0");
+ mReservedUniforms.push_back("shadowMap1");
+ mReservedUniforms.push_back("shadowMap2");
+ mReservedUniforms.push_back("shadowMap3");
+ mReservedUniforms.push_back("shadowMap4");
+ mReservedUniforms.push_back("shadowMap5");
+
+ llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_SHADOW5+1);
+
+ mReservedUniforms.push_back("normalMap");
+ mReservedUniforms.push_back("positionMap");
+ mReservedUniforms.push_back("diffuseRect");
+ mReservedUniforms.push_back("specularRect");
+ mReservedUniforms.push_back("emissiveRect");
+ mReservedUniforms.push_back("exposureMap");
+ mReservedUniforms.push_back("brdfLut");
+ mReservedUniforms.push_back("noiseMap");
+ mReservedUniforms.push_back("lightFunc");
+ mReservedUniforms.push_back("lightMap");
+ mReservedUniforms.push_back("bloomMap");
+ mReservedUniforms.push_back("projectionMap");
+ mReservedUniforms.push_back("norm_mat");
+ mReservedUniforms.push_back("texture_gamma");
+
+ mReservedUniforms.push_back("specular_color");
+ mReservedUniforms.push_back("env_intensity");
+
+ mReservedUniforms.push_back("matrixPalette");
+ mReservedUniforms.push_back("translationPalette");
+
+ mReservedUniforms.push_back("screenTex");
+ mReservedUniforms.push_back("screenDepth");
+ mReservedUniforms.push_back("refTex");
+ mReservedUniforms.push_back("eyeVec");
+ mReservedUniforms.push_back("time");
+ mReservedUniforms.push_back("waveDir1");
+ mReservedUniforms.push_back("waveDir2");
+ mReservedUniforms.push_back("lightDir");
+ mReservedUniforms.push_back("specular");
+ mReservedUniforms.push_back("lightExp");
+ mReservedUniforms.push_back("waterFogColor");
+ mReservedUniforms.push_back("waterFogColorLinear");
+ mReservedUniforms.push_back("waterFogDensity");
+ mReservedUniforms.push_back("waterFogKS");
+ mReservedUniforms.push_back("refScale");
+ mReservedUniforms.push_back("waterHeight");
+ mReservedUniforms.push_back("waterPlane");
+ mReservedUniforms.push_back("normScale");
+ mReservedUniforms.push_back("fresnelScale");
+ mReservedUniforms.push_back("fresnelOffset");
+ mReservedUniforms.push_back("blurMultiplier");
+ mReservedUniforms.push_back("sunAngle");
+ mReservedUniforms.push_back("scaledAngle");
+ mReservedUniforms.push_back("sunAngle2");
+
+ mReservedUniforms.push_back("camPosLocal");
+
+ mReservedUniforms.push_back("gWindDir");
+ mReservedUniforms.push_back("gSinWaveParams");
+ mReservedUniforms.push_back("gGravity");
+
+ mReservedUniforms.push_back("detail_0");
+ mReservedUniforms.push_back("detail_1");
+ mReservedUniforms.push_back("detail_2");
+ mReservedUniforms.push_back("detail_3");
+ mReservedUniforms.push_back("alpha_ramp");
+
+ mReservedUniforms.push_back("origin");
+ mReservedUniforms.push_back("display_gamma");
+
+ mReservedUniforms.push_back("inscatter");
+ mReservedUniforms.push_back("sun_size");
+ mReservedUniforms.push_back("fog_color");
+
+ mReservedUniforms.push_back("transmittance_texture");
+ mReservedUniforms.push_back("scattering_texture");
+ mReservedUniforms.push_back("single_mie_scattering_texture");
+ mReservedUniforms.push_back("irradiance_texture");
+ mReservedUniforms.push_back("blend_factor");
+ mReservedUniforms.push_back("moisture_level");
+ mReservedUniforms.push_back("droplet_radius");
+ mReservedUniforms.push_back("ice_level");
+ mReservedUniforms.push_back("rainbow_map");
+ mReservedUniforms.push_back("halo_map");
+ mReservedUniforms.push_back("moon_brightness");
+ mReservedUniforms.push_back("cloud_variance");
+ mReservedUniforms.push_back("reflection_probe_ambiance");
+ mReservedUniforms.push_back("max_probe_lod");
+
+ mReservedUniforms.push_back("sh_input_r");
+ mReservedUniforms.push_back("sh_input_g");
+ mReservedUniforms.push_back("sh_input_b");
+
+ mReservedUniforms.push_back("sun_moon_glow_factor");
+ mReservedUniforms.push_back("water_edge");
+ mReservedUniforms.push_back("sun_up_factor");
+ mReservedUniforms.push_back("moonlight_color");
+
+ llassert(mReservedUniforms.size() == END_RESERVED_UNIFORMS);
+
+ std::set<std::string> dupe_check;
+
+ for (U32 i = 0; i < mReservedUniforms.size(); ++i)
+ {
+ if (dupe_check.find(mReservedUniforms[i]) != dupe_check.end())
+ {
+ LL_ERRS() << "Duplicate reserved uniform name found: " << mReservedUniforms[i] << LL_ENDL;
+ }
+ dupe_check.insert(mReservedUniforms[i]);
+ }
+}
+
diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h
index e17eacab7e..7d34132203 100644
--- a/indra/llrender/llshadermgr.h
+++ b/indra/llrender/llshadermgr.h
@@ -1,347 +1,347 @@
-/**
- * @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();
-
- // clang-format off
- typedef enum
- { // Shader uniform name, set in LLShaderMgr::initAttribsAndUniforms()
- MODELVIEW_MATRIX = 0, // "modelview_matrix"
- PROJECTION_MATRIX, // "projection_matrix"
- INVERSE_PROJECTION_MATRIX, // "inv_proj"
- MODELVIEW_PROJECTION_MATRIX, // "modelview_projection_matrix"
- INVERSE_MODELVIEW_MATRIX, // "inv_modelview"
- IDENTITY_MATRIX, // "identity_matrix"
- NORMAL_MATRIX, // "normal_matrix"
- TEXTURE_MATRIX0, // "texture_matrix0"
- TEXTURE_MATRIX1, // "texture_matrix1"
- TEXTURE_MATRIX2, // "texture_matrix2"
- TEXTURE_MATRIX3, // "texture_matrix3"
- OBJECT_PLANE_S, // "object_plane_s"
- OBJECT_PLANE_T, // "object_plane_t"
-
- TEXTURE_BASE_COLOR_TRANSFORM, // "texture_base_color_transform" (GLTF)
- TEXTURE_NORMAL_TRANSFORM, // "texture_normal_transform" (GLTF)
- TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, // "texture_metallic_roughness_transform" (GLTF)
- TEXTURE_EMISSIVE_TRANSFORM, // "texture_emissive_transform" (GLTF)
-
- VIEWPORT, // "viewport"
- LIGHT_POSITION, // "light_position"
- LIGHT_DIRECTION, // "light_direction"
- LIGHT_ATTENUATION, // "light_attenuation"
- LIGHT_DEFERRED_ATTENUATION, // "light_deferred_attenuation"
- LIGHT_DIFFUSE, // "light_diffuse"
- LIGHT_AMBIENT, // "light_ambient"
- MULTI_LIGHT_COUNT, // "light_count"
- MULTI_LIGHT, // "light"
- MULTI_LIGHT_COL, // "light_col"
- MULTI_LIGHT_FAR_Z, // "far_z"
- PROJECTOR_MATRIX, // "proj_mat"
- PROJECTOR_NEAR, // "proj_near"
- PROJECTOR_P, // "proj_p"
- PROJECTOR_N, // "proj_n"
- PROJECTOR_ORIGIN, // "proj_origin"
- PROJECTOR_RANGE, // "proj_range"
- PROJECTOR_AMBIANCE, // "proj_ambiance"
- PROJECTOR_SHADOW_INDEX, // "proj_shadow_idx"
- PROJECTOR_SHADOW_FADE, // "shadow_fade"
- PROJECTOR_FOCUS, // "proj_focus"
- PROJECTOR_LOD, // "proj_lod"
- PROJECTOR_AMBIENT_LOD, // "proj_ambient_lod"
- DIFFUSE_COLOR, // "color"
- EMISSIVE_COLOR, // "emissiveColor"
- METALLIC_FACTOR, // "metallicFactor"
- ROUGHNESS_FACTOR, // "roughnessFactor"
- DIFFUSE_MAP, // "diffuseMap"
- ALTERNATE_DIFFUSE_MAP, // "altDiffuseMap"
- SPECULAR_MAP, // "specularMap"
- EMISSIVE_MAP, // "emissiveMap"
- BUMP_MAP, // "bumpMap"
- BUMP_MAP2, // "bumpMap2"
- ENVIRONMENT_MAP, // "environmentMap"
- SCENE_MAP, // "sceneMap"
- SCENE_DEPTH, // "sceneDepth"
- REFLECTION_PROBES, // "reflectionProbes"
- IRRADIANCE_PROBES, // "irradianceProbes"
- CLOUD_NOISE_MAP, // "cloud_noise_texture"
- CLOUD_NOISE_MAP_NEXT, // "cloud_noise_texture_next"
- FULLBRIGHT, // "fullbright"
- LIGHTNORM, // "lightnorm"
- SUNLIGHT_COLOR, // "sunlight_color"
- AMBIENT, // "ambient_color"
- SKY_HDR_SCALE, // "sky_hdr_scale"
- SKY_SUNLIGHT_SCALE, // "sky_sunlight_scale"
- SKY_AMBIENT_SCALE, // "sky_ambient_scale"
- BLUE_HORIZON, // "blue_horizon"
- BLUE_DENSITY, // "blue_density"
- HAZE_HORIZON, // "haze_horizon"
- HAZE_DENSITY, // "haze_density"
- CLOUD_SHADOW, // "cloud_shadow"
- DENSITY_MULTIPLIER, // "density_multiplier"
- DISTANCE_MULTIPLIER, // "distance_multiplier"
- MAX_Y, // "max_y"
- GLOW, // "glow"
- CLOUD_COLOR, // "cloud_color"
- CLOUD_POS_DENSITY1, // "cloud_pos_density1"
- CLOUD_POS_DENSITY2, // "cloud_pos_density2"
- CLOUD_SCALE, // "cloud_scale"
- GAMMA, // "gamma"
- SCENE_LIGHT_STRENGTH, // "scene_light_strength"
- LIGHT_CENTER, // "center"
- LIGHT_SIZE, // "size"
- LIGHT_FALLOFF, // "falloff"
- BOX_CENTER, // "box_center"
- BOX_SIZE, // "box_size"
-
- GLOW_MIN_LUMINANCE, // "minLuminance"
- GLOW_MAX_EXTRACT_ALPHA, // "maxExtractAlpha"
- GLOW_LUM_WEIGHTS, // "lumWeights"
- GLOW_WARMTH_WEIGHTS, // "warmthWeights"
- GLOW_WARMTH_AMOUNT, // "warmthAmount"
- GLOW_STRENGTH, // "glowStrength"
- GLOW_DELTA, // "glowDelta"
- GLOW_NOISE_MAP, // "glowNoiseMap"
-
- MINIMUM_ALPHA, // "minimum_alpha"
- EMISSIVE_BRIGHTNESS, // "emissive_brightness"
-
- DEFERRED_SHADOW_MATRIX, // "shadow_matrix"
- DEFERRED_ENV_MAT, // "env_mat"
- DEFERRED_SHADOW_CLIP, // "shadow_clip"
- DEFERRED_SUN_WASH, // "sun_wash"
- DEFERRED_SHADOW_NOISE, // "shadow_noise"
- DEFERRED_BLUR_SIZE, // "blur_size"
- DEFERRED_SSAO_RADIUS, // "ssao_radius"
- DEFERRED_SSAO_MAX_RADIUS, // "ssao_max_radius"
- DEFERRED_SSAO_FACTOR, // "ssao_factor"
- DEFERRED_SSAO_FACTOR_INV, // "ssao_factor_inv"
- DEFERRED_SSAO_EFFECT_MAT, // "ssao_effect_mat"
- DEFERRED_SCREEN_RES, // "screen_res"
- DEFERRED_NEAR_CLIP, // "near_clip"
- DEFERRED_SHADOW_OFFSET, // "shadow_offset"
- DEFERRED_SHADOW_BIAS, // "shadow_bias"
- DEFERRED_SPOT_SHADOW_BIAS, // "spot_shadow_bias"
- DEFERRED_SPOT_SHADOW_OFFSET, // "spot_shadow_offset"
- DEFERRED_SUN_DIR, // "sun_dir"
- DEFERRED_MOON_DIR, // "moon_dir"
- DEFERRED_SHADOW_RES, // "shadow_res"
- DEFERRED_PROJ_SHADOW_RES, // "proj_shadow_res"
- DEFERRED_DEPTH_CUTOFF, // "depth_cutoff"
- DEFERRED_NORM_CUTOFF, // "norm_cutoff"
- DEFERRED_SHADOW_TARGET_WIDTH, // "shadow_target_width"
-
- DEFERRED_SSR_ITR_COUNT, // "iterationCount"
- DEFERRED_SSR_RAY_STEP, // "rayStep"
- DEFERRED_SSR_DIST_BIAS, // "distanceBias"
- DEFERRED_SSR_REJECT_BIAS, // "depthRejectBias"
- DEFERRED_SSR_GLOSSY_SAMPLES, // "glossySampleCount"
- DEFERRED_SSR_NOISE_SINE, // "noiseSine"
- DEFERRED_SSR_ADAPTIVE_STEP_MULT, // "adaptiveStepMultiplier"
-
- MODELVIEW_DELTA_MATRIX, // "modelview_delta"
- INVERSE_MODELVIEW_DELTA_MATRIX, // "inv_modelview_delta"
- CUBE_SNAPSHOT, // "cube_snapshot"
-
- FXAA_TC_SCALE, // "tc_scale"
- FXAA_RCP_SCREEN_RES, // "rcp_screen_res"
- FXAA_RCP_FRAME_OPT, // "rcp_frame_opt"
- FXAA_RCP_FRAME_OPT2, // "rcp_frame_opt2"
-
- DOF_FOCAL_DISTANCE, // "focal_distance"
- DOF_BLUR_CONSTANT, // "blur_constant"
- DOF_TAN_PIXEL_ANGLE, // "tan_pixel_angle"
- DOF_MAGNIFICATION, // "magnification"
- DOF_MAX_COF, // "max_cof"
- DOF_RES_SCALE, // "res_scale"
- DOF_WIDTH, // "dof_width"
- DOF_HEIGHT, // "dof_height"
-
- DEFERRED_DEPTH, // "depthMap"
- DEFERRED_SHADOW0, // "shadowMap0"
- DEFERRED_SHADOW1, // "shadowMap1"
- DEFERRED_SHADOW2, // "shadowMap2"
- DEFERRED_SHADOW3, // "shadowMap3"
- DEFERRED_SHADOW4, // "shadowMap4"
- DEFERRED_SHADOW5, // "shadowMap5"
- DEFERRED_NORMAL, // "normalMap"
- DEFERRED_POSITION, // "positionMap"
- DEFERRED_DIFFUSE, // "diffuseRect"
- DEFERRED_SPECULAR, // "specularRect"
- DEFERRED_EMISSIVE, // "emissiveRect"
- EXPOSURE_MAP, // "exposureMap"
- DEFERRED_BRDF_LUT, // "brdfLut"
- DEFERRED_NOISE, // "noiseMap"
- DEFERRED_LIGHTFUNC, // "lightFunc"
- DEFERRED_LIGHT, // "lightMap"
- DEFERRED_BLOOM, // "bloomMap"
- DEFERRED_PROJECTION, // "projectionMap"
- DEFERRED_NORM_MATRIX, // "norm_mat"
- TEXTURE_GAMMA, // "texture_gamma"
- SPECULAR_COLOR, // "specular_color"
- ENVIRONMENT_INTENSITY, // "env_intensity"
-
- AVATAR_MATRIX, // "matrixPalette"
- AVATAR_TRANSLATION, // "translationPalette"
-
- WATER_SCREENTEX, // "screenTex"
- WATER_SCREENDEPTH, // "screenDepth"
- WATER_REFTEX, // "refTex"
- WATER_EYEVEC, // "eyeVec"
- WATER_TIME, // "time"
- WATER_WAVE_DIR1, // "waveDir1"
- WATER_WAVE_DIR2, // "waveDir2"
- WATER_LIGHT_DIR, // "lightDir"
- WATER_SPECULAR, // "specular"
- WATER_SPECULAR_EXP, // "lightExp"
- WATER_FOGCOLOR, // "waterFogColor"
- WATER_FOGCOLOR_LINEAR, // "waterFogColorLinear"
- WATER_FOGDENSITY, // "waterFogDensity"
- WATER_FOGKS, // "waterFogKS"
- WATER_REFSCALE, // "refScale"
- WATER_WATERHEIGHT, // "waterHeight"
- WATER_WATERPLANE, // "waterPlane"
- WATER_NORM_SCALE, // "normScale"
- WATER_FRESNEL_SCALE, // "fresnelScale"
- WATER_FRESNEL_OFFSET, // "fresnelOffset"
- WATER_BLUR_MULTIPLIER, // "blurMultiplier"
- WATER_SUN_ANGLE, // "sunAngle"
- WATER_SCALED_ANGLE, // "scaledAngle"
- WATER_SUN_ANGLE2, // "sunAngle2"
-
- WL_CAMPOSLOCAL, // "camPosLocal"
-
- AVATAR_WIND, // "gWindDir"
- AVATAR_SINWAVE, // "gSinWaveParams"
- AVATAR_GRAVITY, // "gGravity"
-
- TERRAIN_DETAIL0, // "detail_0"
- TERRAIN_DETAIL1, // "detail_1"
- TERRAIN_DETAIL2, // "detail_2"
- TERRAIN_DETAIL3, // "detail_3"
- TERRAIN_ALPHARAMP, // "alpha_ramp"
-
- SHINY_ORIGIN, // "origin"
- DISPLAY_GAMMA, // "display_gamma"
-
- INSCATTER_RT, // "inscatter"
- SUN_SIZE, // "sun_size"
- FOG_COLOR, // "fog_color"
-
- // precomputed textures
- TRANSMITTANCE_TEX, // "transmittance_texture"
- SCATTER_TEX, // "scattering_texture"
- SINGLE_MIE_SCATTER_TEX, // "single_mie_scattering_texture"
- ILLUMINANCE_TEX, // "irradiance_texture"
- BLEND_FACTOR, // "blend_factor"
-
- MOISTURE_LEVEL, // "moisture_level"
- DROPLET_RADIUS, // "droplet_radius"
- ICE_LEVEL, // "ice_level"
- RAINBOW_MAP, // "rainbow_map"
- HALO_MAP, // "halo_map"
-
- MOON_BRIGHTNESS, // "moon_brightness"
-
- CLOUD_VARIANCE, // "cloud_variance"
-
- REFLECTION_PROBE_AMBIANCE, // "reflection_probe_ambiance"
- REFLECTION_PROBE_MAX_LOD, // "max_probe_lod"
- SH_INPUT_L1R, // "sh_input_r"
- SH_INPUT_L1G, // "sh_input_g"
- SH_INPUT_L1B, // "sh_input_b"
-
- SUN_MOON_GLOW_FACTOR, // "sun_moon_glow_factor"
- WATER_EDGE_FACTOR, // "water_edge"
- SUN_UP_FACTOR, // "sun_up_factor"
- MOONLIGHT_COLOR, // "moonlight_color"
- END_RESERVED_UNIFORMS
- } eGLSLReservedUniforms;
- // clang-format on
-
- // singleton pattern implementation
- static LLShaderMgr * instance();
-
- virtual void initAttribsAndUniforms(void);
-
- bool attachShaderFeatures(LLGLSLShader * shader);
- void dumpObjectLog(GLuint ret, bool warns = true, const std::string& filename = "");
- void dumpShaderSource(U32 shader_code_count, GLchar** shader_code_text);
- bool linkProgramObject(GLuint obj, bool suppress_errors = false);
- bool validateProgramObject(GLuint obj);
- GLuint loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type, std::map<std::string, std::string>* defines = NULL, S32 texture_index_channels = -1);
-
- // 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
-
- void initShaderCache(bool enabled, const LLUUID& old_cache_version, const LLUUID& current_cache_version);
- void clearShaderCache();
- void persistShaderCacheMetadata();
-
- bool loadCachedProgramBinary(LLGLSLShader* shader);
- bool saveCachedProgramBinary(LLGLSLShader* shader);
-
-public:
- // Map of shader names to compiled
- std::map<std::string, GLuint> mVertexShaderObjects;
- std::map<std::string, GLuint> mFragmentShaderObjects;
-
- //global (reserved slot) shader parameters
- std::vector<std::string> mReservedAttribs;
-
- std::vector<std::string> mReservedUniforms;
-
- struct ProgramBinaryData
- {
- GLsizei mBinaryLength = 0;
- GLenum mBinaryFormat = 0;
- F32 mLastUsedTime = 0.0;
- };
- std::map<LLUUID, ProgramBinaryData> mShaderBinaryCache;
- bool mShaderCacheInitialized = false;
- bool mShaderCacheEnabled = false;
- std::string mShaderCacheDir;
-
-protected:
-
- // our parameter manager singleton instance
- static LLShaderMgr * sInstance;
-
-}; //LLShaderMgr
-
-#endif
+/**
+ * @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();
+
+ // clang-format off
+ typedef enum
+ { // Shader uniform name, set in LLShaderMgr::initAttribsAndUniforms()
+ MODELVIEW_MATRIX = 0, // "modelview_matrix"
+ PROJECTION_MATRIX, // "projection_matrix"
+ INVERSE_PROJECTION_MATRIX, // "inv_proj"
+ MODELVIEW_PROJECTION_MATRIX, // "modelview_projection_matrix"
+ INVERSE_MODELVIEW_MATRIX, // "inv_modelview"
+ IDENTITY_MATRIX, // "identity_matrix"
+ NORMAL_MATRIX, // "normal_matrix"
+ TEXTURE_MATRIX0, // "texture_matrix0"
+ TEXTURE_MATRIX1, // "texture_matrix1"
+ TEXTURE_MATRIX2, // "texture_matrix2"
+ TEXTURE_MATRIX3, // "texture_matrix3"
+ OBJECT_PLANE_S, // "object_plane_s"
+ OBJECT_PLANE_T, // "object_plane_t"
+
+ TEXTURE_BASE_COLOR_TRANSFORM, // "texture_base_color_transform" (GLTF)
+ TEXTURE_NORMAL_TRANSFORM, // "texture_normal_transform" (GLTF)
+ TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, // "texture_metallic_roughness_transform" (GLTF)
+ TEXTURE_EMISSIVE_TRANSFORM, // "texture_emissive_transform" (GLTF)
+
+ VIEWPORT, // "viewport"
+ LIGHT_POSITION, // "light_position"
+ LIGHT_DIRECTION, // "light_direction"
+ LIGHT_ATTENUATION, // "light_attenuation"
+ LIGHT_DEFERRED_ATTENUATION, // "light_deferred_attenuation"
+ LIGHT_DIFFUSE, // "light_diffuse"
+ LIGHT_AMBIENT, // "light_ambient"
+ MULTI_LIGHT_COUNT, // "light_count"
+ MULTI_LIGHT, // "light"
+ MULTI_LIGHT_COL, // "light_col"
+ MULTI_LIGHT_FAR_Z, // "far_z"
+ PROJECTOR_MATRIX, // "proj_mat"
+ PROJECTOR_NEAR, // "proj_near"
+ PROJECTOR_P, // "proj_p"
+ PROJECTOR_N, // "proj_n"
+ PROJECTOR_ORIGIN, // "proj_origin"
+ PROJECTOR_RANGE, // "proj_range"
+ PROJECTOR_AMBIANCE, // "proj_ambiance"
+ PROJECTOR_SHADOW_INDEX, // "proj_shadow_idx"
+ PROJECTOR_SHADOW_FADE, // "shadow_fade"
+ PROJECTOR_FOCUS, // "proj_focus"
+ PROJECTOR_LOD, // "proj_lod"
+ PROJECTOR_AMBIENT_LOD, // "proj_ambient_lod"
+ DIFFUSE_COLOR, // "color"
+ EMISSIVE_COLOR, // "emissiveColor"
+ METALLIC_FACTOR, // "metallicFactor"
+ ROUGHNESS_FACTOR, // "roughnessFactor"
+ DIFFUSE_MAP, // "diffuseMap"
+ ALTERNATE_DIFFUSE_MAP, // "altDiffuseMap"
+ SPECULAR_MAP, // "specularMap"
+ EMISSIVE_MAP, // "emissiveMap"
+ BUMP_MAP, // "bumpMap"
+ BUMP_MAP2, // "bumpMap2"
+ ENVIRONMENT_MAP, // "environmentMap"
+ SCENE_MAP, // "sceneMap"
+ SCENE_DEPTH, // "sceneDepth"
+ REFLECTION_PROBES, // "reflectionProbes"
+ IRRADIANCE_PROBES, // "irradianceProbes"
+ CLOUD_NOISE_MAP, // "cloud_noise_texture"
+ CLOUD_NOISE_MAP_NEXT, // "cloud_noise_texture_next"
+ FULLBRIGHT, // "fullbright"
+ LIGHTNORM, // "lightnorm"
+ SUNLIGHT_COLOR, // "sunlight_color"
+ AMBIENT, // "ambient_color"
+ SKY_HDR_SCALE, // "sky_hdr_scale"
+ SKY_SUNLIGHT_SCALE, // "sky_sunlight_scale"
+ SKY_AMBIENT_SCALE, // "sky_ambient_scale"
+ BLUE_HORIZON, // "blue_horizon"
+ BLUE_DENSITY, // "blue_density"
+ HAZE_HORIZON, // "haze_horizon"
+ HAZE_DENSITY, // "haze_density"
+ CLOUD_SHADOW, // "cloud_shadow"
+ DENSITY_MULTIPLIER, // "density_multiplier"
+ DISTANCE_MULTIPLIER, // "distance_multiplier"
+ MAX_Y, // "max_y"
+ GLOW, // "glow"
+ CLOUD_COLOR, // "cloud_color"
+ CLOUD_POS_DENSITY1, // "cloud_pos_density1"
+ CLOUD_POS_DENSITY2, // "cloud_pos_density2"
+ CLOUD_SCALE, // "cloud_scale"
+ GAMMA, // "gamma"
+ SCENE_LIGHT_STRENGTH, // "scene_light_strength"
+ LIGHT_CENTER, // "center"
+ LIGHT_SIZE, // "size"
+ LIGHT_FALLOFF, // "falloff"
+ BOX_CENTER, // "box_center"
+ BOX_SIZE, // "box_size"
+
+ GLOW_MIN_LUMINANCE, // "minLuminance"
+ GLOW_MAX_EXTRACT_ALPHA, // "maxExtractAlpha"
+ GLOW_LUM_WEIGHTS, // "lumWeights"
+ GLOW_WARMTH_WEIGHTS, // "warmthWeights"
+ GLOW_WARMTH_AMOUNT, // "warmthAmount"
+ GLOW_STRENGTH, // "glowStrength"
+ GLOW_DELTA, // "glowDelta"
+ GLOW_NOISE_MAP, // "glowNoiseMap"
+
+ MINIMUM_ALPHA, // "minimum_alpha"
+ EMISSIVE_BRIGHTNESS, // "emissive_brightness"
+
+ DEFERRED_SHADOW_MATRIX, // "shadow_matrix"
+ DEFERRED_ENV_MAT, // "env_mat"
+ DEFERRED_SHADOW_CLIP, // "shadow_clip"
+ DEFERRED_SUN_WASH, // "sun_wash"
+ DEFERRED_SHADOW_NOISE, // "shadow_noise"
+ DEFERRED_BLUR_SIZE, // "blur_size"
+ DEFERRED_SSAO_RADIUS, // "ssao_radius"
+ DEFERRED_SSAO_MAX_RADIUS, // "ssao_max_radius"
+ DEFERRED_SSAO_FACTOR, // "ssao_factor"
+ DEFERRED_SSAO_FACTOR_INV, // "ssao_factor_inv"
+ DEFERRED_SSAO_EFFECT_MAT, // "ssao_effect_mat"
+ DEFERRED_SCREEN_RES, // "screen_res"
+ DEFERRED_NEAR_CLIP, // "near_clip"
+ DEFERRED_SHADOW_OFFSET, // "shadow_offset"
+ DEFERRED_SHADOW_BIAS, // "shadow_bias"
+ DEFERRED_SPOT_SHADOW_BIAS, // "spot_shadow_bias"
+ DEFERRED_SPOT_SHADOW_OFFSET, // "spot_shadow_offset"
+ DEFERRED_SUN_DIR, // "sun_dir"
+ DEFERRED_MOON_DIR, // "moon_dir"
+ DEFERRED_SHADOW_RES, // "shadow_res"
+ DEFERRED_PROJ_SHADOW_RES, // "proj_shadow_res"
+ DEFERRED_DEPTH_CUTOFF, // "depth_cutoff"
+ DEFERRED_NORM_CUTOFF, // "norm_cutoff"
+ DEFERRED_SHADOW_TARGET_WIDTH, // "shadow_target_width"
+
+ DEFERRED_SSR_ITR_COUNT, // "iterationCount"
+ DEFERRED_SSR_RAY_STEP, // "rayStep"
+ DEFERRED_SSR_DIST_BIAS, // "distanceBias"
+ DEFERRED_SSR_REJECT_BIAS, // "depthRejectBias"
+ DEFERRED_SSR_GLOSSY_SAMPLES, // "glossySampleCount"
+ DEFERRED_SSR_NOISE_SINE, // "noiseSine"
+ DEFERRED_SSR_ADAPTIVE_STEP_MULT, // "adaptiveStepMultiplier"
+
+ MODELVIEW_DELTA_MATRIX, // "modelview_delta"
+ INVERSE_MODELVIEW_DELTA_MATRIX, // "inv_modelview_delta"
+ CUBE_SNAPSHOT, // "cube_snapshot"
+
+ FXAA_TC_SCALE, // "tc_scale"
+ FXAA_RCP_SCREEN_RES, // "rcp_screen_res"
+ FXAA_RCP_FRAME_OPT, // "rcp_frame_opt"
+ FXAA_RCP_FRAME_OPT2, // "rcp_frame_opt2"
+
+ DOF_FOCAL_DISTANCE, // "focal_distance"
+ DOF_BLUR_CONSTANT, // "blur_constant"
+ DOF_TAN_PIXEL_ANGLE, // "tan_pixel_angle"
+ DOF_MAGNIFICATION, // "magnification"
+ DOF_MAX_COF, // "max_cof"
+ DOF_RES_SCALE, // "res_scale"
+ DOF_WIDTH, // "dof_width"
+ DOF_HEIGHT, // "dof_height"
+
+ DEFERRED_DEPTH, // "depthMap"
+ DEFERRED_SHADOW0, // "shadowMap0"
+ DEFERRED_SHADOW1, // "shadowMap1"
+ DEFERRED_SHADOW2, // "shadowMap2"
+ DEFERRED_SHADOW3, // "shadowMap3"
+ DEFERRED_SHADOW4, // "shadowMap4"
+ DEFERRED_SHADOW5, // "shadowMap5"
+ DEFERRED_NORMAL, // "normalMap"
+ DEFERRED_POSITION, // "positionMap"
+ DEFERRED_DIFFUSE, // "diffuseRect"
+ DEFERRED_SPECULAR, // "specularRect"
+ DEFERRED_EMISSIVE, // "emissiveRect"
+ EXPOSURE_MAP, // "exposureMap"
+ DEFERRED_BRDF_LUT, // "brdfLut"
+ DEFERRED_NOISE, // "noiseMap"
+ DEFERRED_LIGHTFUNC, // "lightFunc"
+ DEFERRED_LIGHT, // "lightMap"
+ DEFERRED_BLOOM, // "bloomMap"
+ DEFERRED_PROJECTION, // "projectionMap"
+ DEFERRED_NORM_MATRIX, // "norm_mat"
+ TEXTURE_GAMMA, // "texture_gamma"
+ SPECULAR_COLOR, // "specular_color"
+ ENVIRONMENT_INTENSITY, // "env_intensity"
+
+ AVATAR_MATRIX, // "matrixPalette"
+ AVATAR_TRANSLATION, // "translationPalette"
+
+ WATER_SCREENTEX, // "screenTex"
+ WATER_SCREENDEPTH, // "screenDepth"
+ WATER_REFTEX, // "refTex"
+ WATER_EYEVEC, // "eyeVec"
+ WATER_TIME, // "time"
+ WATER_WAVE_DIR1, // "waveDir1"
+ WATER_WAVE_DIR2, // "waveDir2"
+ WATER_LIGHT_DIR, // "lightDir"
+ WATER_SPECULAR, // "specular"
+ WATER_SPECULAR_EXP, // "lightExp"
+ WATER_FOGCOLOR, // "waterFogColor"
+ WATER_FOGCOLOR_LINEAR, // "waterFogColorLinear"
+ WATER_FOGDENSITY, // "waterFogDensity"
+ WATER_FOGKS, // "waterFogKS"
+ WATER_REFSCALE, // "refScale"
+ WATER_WATERHEIGHT, // "waterHeight"
+ WATER_WATERPLANE, // "waterPlane"
+ WATER_NORM_SCALE, // "normScale"
+ WATER_FRESNEL_SCALE, // "fresnelScale"
+ WATER_FRESNEL_OFFSET, // "fresnelOffset"
+ WATER_BLUR_MULTIPLIER, // "blurMultiplier"
+ WATER_SUN_ANGLE, // "sunAngle"
+ WATER_SCALED_ANGLE, // "scaledAngle"
+ WATER_SUN_ANGLE2, // "sunAngle2"
+
+ WL_CAMPOSLOCAL, // "camPosLocal"
+
+ AVATAR_WIND, // "gWindDir"
+ AVATAR_SINWAVE, // "gSinWaveParams"
+ AVATAR_GRAVITY, // "gGravity"
+
+ TERRAIN_DETAIL0, // "detail_0"
+ TERRAIN_DETAIL1, // "detail_1"
+ TERRAIN_DETAIL2, // "detail_2"
+ TERRAIN_DETAIL3, // "detail_3"
+ TERRAIN_ALPHARAMP, // "alpha_ramp"
+
+ SHINY_ORIGIN, // "origin"
+ DISPLAY_GAMMA, // "display_gamma"
+
+ INSCATTER_RT, // "inscatter"
+ SUN_SIZE, // "sun_size"
+ FOG_COLOR, // "fog_color"
+
+ // precomputed textures
+ TRANSMITTANCE_TEX, // "transmittance_texture"
+ SCATTER_TEX, // "scattering_texture"
+ SINGLE_MIE_SCATTER_TEX, // "single_mie_scattering_texture"
+ ILLUMINANCE_TEX, // "irradiance_texture"
+ BLEND_FACTOR, // "blend_factor"
+
+ MOISTURE_LEVEL, // "moisture_level"
+ DROPLET_RADIUS, // "droplet_radius"
+ ICE_LEVEL, // "ice_level"
+ RAINBOW_MAP, // "rainbow_map"
+ HALO_MAP, // "halo_map"
+
+ MOON_BRIGHTNESS, // "moon_brightness"
+
+ CLOUD_VARIANCE, // "cloud_variance"
+
+ REFLECTION_PROBE_AMBIANCE, // "reflection_probe_ambiance"
+ REFLECTION_PROBE_MAX_LOD, // "max_probe_lod"
+ SH_INPUT_L1R, // "sh_input_r"
+ SH_INPUT_L1G, // "sh_input_g"
+ SH_INPUT_L1B, // "sh_input_b"
+
+ SUN_MOON_GLOW_FACTOR, // "sun_moon_glow_factor"
+ WATER_EDGE_FACTOR, // "water_edge"
+ SUN_UP_FACTOR, // "sun_up_factor"
+ MOONLIGHT_COLOR, // "moonlight_color"
+ END_RESERVED_UNIFORMS
+ } eGLSLReservedUniforms;
+ // clang-format on
+
+ // singleton pattern implementation
+ static LLShaderMgr * instance();
+
+ virtual void initAttribsAndUniforms(void);
+
+ bool attachShaderFeatures(LLGLSLShader * shader);
+ void dumpObjectLog(GLuint ret, bool warns = true, const std::string& filename = "");
+ void dumpShaderSource(U32 shader_code_count, GLchar** shader_code_text);
+ bool linkProgramObject(GLuint obj, bool suppress_errors = false);
+ bool validateProgramObject(GLuint obj);
+ GLuint loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type, std::map<std::string, std::string>* defines = NULL, S32 texture_index_channels = -1);
+
+ // 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
+
+ void initShaderCache(bool enabled, const LLUUID& old_cache_version, const LLUUID& current_cache_version);
+ void clearShaderCache();
+ void persistShaderCacheMetadata();
+
+ bool loadCachedProgramBinary(LLGLSLShader* shader);
+ bool saveCachedProgramBinary(LLGLSLShader* shader);
+
+public:
+ // Map of shader names to compiled
+ std::map<std::string, GLuint> mVertexShaderObjects;
+ std::map<std::string, GLuint> mFragmentShaderObjects;
+
+ //global (reserved slot) shader parameters
+ std::vector<std::string> mReservedAttribs;
+
+ std::vector<std::string> mReservedUniforms;
+
+ struct ProgramBinaryData
+ {
+ GLsizei mBinaryLength = 0;
+ GLenum mBinaryFormat = 0;
+ F32 mLastUsedTime = 0.0;
+ };
+ std::map<LLUUID, ProgramBinaryData> mShaderBinaryCache;
+ bool mShaderCacheInitialized = false;
+ bool mShaderCacheEnabled = false;
+ std::string mShaderCacheDir;
+
+protected:
+
+ // our parameter manager singleton instance
+ static LLShaderMgr * sInstance;
+
+}; //LLShaderMgr
+
+#endif
diff --git a/indra/llrender/lltexturemanagerbridge.h b/indra/llrender/lltexturemanagerbridge.h
index 4ead3ff23c..e742324e4a 100644
--- a/indra/llrender/lltexturemanagerbridge.h
+++ b/indra/llrender/lltexturemanagerbridge.h
@@ -1,47 +1,47 @@
-/**
- * @file lltexturemanagerbridge.h
- * @brief Bridge to an application-specific texture manager.
- *
- * $LicenseInfo:firstyear=2012&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_TEXTUREMANAGERBRIDGE_H
-#define LL_TEXTUREMANAGERBRIDGE_H
-
-#include "llpointer.h"
-#include "llgltexture.h"
-
-// Abstract bridge interface
-class LLTextureManagerBridge
-{
-public:
- virtual ~LLTextureManagerBridge() {}
-
- virtual LLPointer<LLGLTexture> getLocalTexture(bool usemipmaps = true, bool generate_gl_tex = true) = 0;
- virtual LLPointer<LLGLTexture> getLocalTexture(const U32 width, const U32 height, const U8 components, bool usemipmaps, bool generate_gl_tex = true) = 0;
- virtual LLGLTexture* getFetchedTexture(const LLUUID &image_id) = 0;
-};
-
-extern LLTextureManagerBridge* gTextureManagerBridgep;
-
-#endif // LL_TEXTUREMANAGERBRIDGE_H
-
+/**
+ * @file lltexturemanagerbridge.h
+ * @brief Bridge to an application-specific texture manager.
+ *
+ * $LicenseInfo:firstyear=2012&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_TEXTUREMANAGERBRIDGE_H
+#define LL_TEXTUREMANAGERBRIDGE_H
+
+#include "llpointer.h"
+#include "llgltexture.h"
+
+// Abstract bridge interface
+class LLTextureManagerBridge
+{
+public:
+ virtual ~LLTextureManagerBridge() {}
+
+ virtual LLPointer<LLGLTexture> getLocalTexture(bool usemipmaps = true, bool generate_gl_tex = true) = 0;
+ virtual LLPointer<LLGLTexture> getLocalTexture(const U32 width, const U32 height, const U8 components, bool usemipmaps, bool generate_gl_tex = true) = 0;
+ virtual LLGLTexture* getFetchedTexture(const LLUUID &image_id) = 0;
+};
+
+extern LLTextureManagerBridge* gTextureManagerBridgep;
+
+#endif // LL_TEXTUREMANAGERBRIDGE_H
+
diff --git a/indra/llrender/lluiimage.inl b/indra/llrender/lluiimage.inl
index f220dd9d08..dff1fcdfcc 100644
--- a/indra/llrender/lluiimage.inl
+++ b/indra/llrender/lluiimage.inl
@@ -1,77 +1,77 @@
-/**
- * @file lluiimage.inl
- * @brief UI inline func 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$
- */
-
-void LLUIImage::draw(S32 x, S32 y, const LLColor4& color) const
-{
- draw(x, y, getWidth(), getHeight(), color);
-}
-
-void LLUIImage::draw(S32 x, S32 y, S32 width, S32 height, const LLColor4& color) const
-{
- gl_draw_scaled_image_with_border(
- x, y,
- width, height,
- mImage,
- color,
- false,
- mClipRegion,
- mScaleRegion,
- mScaleStyle == SCALE_INNER);
-}
-
-void LLUIImage::drawSolid(S32 x, S32 y, S32 width, S32 height, const LLColor4& color) const
-{
- gl_draw_scaled_image_with_border(
- x, y,
- width, height,
- mImage,
- color,
- true,
- mClipRegion,
- mScaleRegion,
- mScaleStyle == SCALE_INNER);
-}
-
-void LLUIImage::drawBorder(S32 x, S32 y, S32 width, S32 height, const LLColor4& color, S32 border_width) const
-{
- LLRect border_rect;
- border_rect.setOriginAndSize(x, y, width, height);
- border_rect.stretch(border_width, border_width);
- drawSolid(border_rect, color);
-}
-
-// returns dimensions of underlying textures, which might not be equal to ui image portion
-S32 LLUIImage::getTextureWidth() const
-{
- mCachedW = (mCachedW == -1) ? getWidth() : mCachedW;
- return mCachedW;
-}
-
-S32 LLUIImage::getTextureHeight() const
-{
- mCachedH = (mCachedH == -1) ? getHeight() : mCachedH;
- return mCachedH;
-}
+/**
+ * @file lluiimage.inl
+ * @brief UI inline func 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$
+ */
+
+void LLUIImage::draw(S32 x, S32 y, const LLColor4& color) const
+{
+ draw(x, y, getWidth(), getHeight(), color);
+}
+
+void LLUIImage::draw(S32 x, S32 y, S32 width, S32 height, const LLColor4& color) const
+{
+ gl_draw_scaled_image_with_border(
+ x, y,
+ width, height,
+ mImage,
+ color,
+ false,
+ mClipRegion,
+ mScaleRegion,
+ mScaleStyle == SCALE_INNER);
+}
+
+void LLUIImage::drawSolid(S32 x, S32 y, S32 width, S32 height, const LLColor4& color) const
+{
+ gl_draw_scaled_image_with_border(
+ x, y,
+ width, height,
+ mImage,
+ color,
+ true,
+ mClipRegion,
+ mScaleRegion,
+ mScaleStyle == SCALE_INNER);
+}
+
+void LLUIImage::drawBorder(S32 x, S32 y, S32 width, S32 height, const LLColor4& color, S32 border_width) const
+{
+ LLRect border_rect;
+ border_rect.setOriginAndSize(x, y, width, height);
+ border_rect.stretch(border_width, border_width);
+ drawSolid(border_rect, color);
+}
+
+// returns dimensions of underlying textures, which might not be equal to ui image portion
+S32 LLUIImage::getTextureWidth() const
+{
+ mCachedW = (mCachedW == -1) ? getWidth() : mCachedW;
+ return mCachedW;
+}
+
+S32 LLUIImage::getTextureHeight() const
+{
+ mCachedH = (mCachedH == -1) ? getHeight() : mCachedH;
+ return mCachedH;
+}
diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h
index 9bcfb3a958..a08ec2bb7a 100644
--- a/indra/llrender/llvertexbuffer.h
+++ b/indra/llrender/llvertexbuffer.h
@@ -1,276 +1,276 @@
-/**
- * @file llvertexbuffer.h
- * @brief LLVertexBuffer wrapper for OpengGL vertex buffer objects
- *
- * $LicenseInfo:firstyear=2003&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_LLVERTEXBUFFER_H
-#define LL_LLVERTEXBUFFER_H
-
-#include "llgl.h"
-#include "v2math.h"
-#include "v3math.h"
-#include "v4math.h"
-#include "v4coloru.h"
-#include "llstrider.h"
-#include "llrender.h"
-#include "lltrace.h"
-#include <set>
-#include <vector>
-#include <list>
-
-#define LL_MAX_VERTEX_ATTRIB_LOCATION 64
-
-//============================================================================
-// NOTES
-// Threading:
-// All constructors should take an 'create' paramater which should only be
-// 'true' when called from the main thread. Otherwise createGLBuffer() will
-// be called as soon as getVertexPointer(), etc is called (which MUST ONLY be
-// called from the main (i.e OpenGL) thread)
-
-
-//============================================================================
-// base class
-class LLPrivateMemoryPool;
-class LLVertexBuffer final : public LLRefCount
-{
-public:
- struct MappedRegion
- {
- U32 mStart;
- U32 mEnd;
- };
-
- LLVertexBuffer(const LLVertexBuffer& rhs)
- {
- *this = rhs;
- }
-
- const LLVertexBuffer& operator=(const LLVertexBuffer& rhs)
- {
- LL_ERRS() << "Illegal operation!" << LL_ENDL;
- return *this;
- }
-
- static void initClass(LLWindow* window);
- static void cleanupClass();
- static void setupClientArrays(U32 data_mask);
- static void drawArrays(U32 mode, const std::vector<LLVector3>& pos);
- static void drawElements(U32 mode, const LLVector4a* pos, const LLVector2* tc, U32 num_indices, const U16* indicesp);
-
- static void unbind(); //unbind any bound vertex buffer
-
- //get the size of a vertex with the given typemask
- static U32 calcVertexSize(const U32& typemask);
-
- //get the size of a buffer with the given typemask and vertex count
- //fill offsets with the offset of each vertex component array into the buffer
- // indexed by the following enum
- static U32 calcOffsets(const U32& typemask, U32* offsets, U32 num_vertices);
-
- //WARNING -- when updating these enums you MUST
- // 1 - update LLVertexBuffer::sTypeSize
- // 2 - update LLVertexBuffer::vb_type_name
- // 3 - add a strider accessor
- // 4 - modify LLVertexBuffer::setupVertexBuffer
- // 6 - modify LLViewerShaderMgr::mReservedAttribs
-
- // clang-format off
- enum AttributeType { // Shader attribute name, set in LLShaderMgr::initAttribsAndUniforms()
- TYPE_VERTEX = 0, // "position"
- TYPE_NORMAL, // "normal"
- TYPE_TEXCOORD0, // "texcoord0"
- TYPE_TEXCOORD1, // "texcoord1"
- TYPE_TEXCOORD2, // "texcoord2"
- TYPE_TEXCOORD3, // "texcoord3"
- TYPE_COLOR, // "diffuse_color"
- TYPE_EMISSIVE, // "emissive"
- TYPE_TANGENT, // "tangent"
- TYPE_WEIGHT, // "weight"
- TYPE_WEIGHT4, // "weight4"
- TYPE_CLOTHWEIGHT, // "clothing"
- TYPE_TEXTURE_INDEX, // "texture_index"
- TYPE_MAX, // TYPE_MAX is the size/boundary marker for attributes that go in the vertex buffer
- TYPE_INDEX, // TYPE_INDEX is beyond _MAX because it lives in a separate (index) buffer
- };
- // clang-format on
-
- enum {
- MAP_VERTEX = (1<<TYPE_VERTEX),
- MAP_NORMAL = (1<<TYPE_NORMAL),
- 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),
- MAP_EMISSIVE = (1<<TYPE_EMISSIVE),
- MAP_TANGENT = (1<<TYPE_TANGENT),
- MAP_WEIGHT = (1<<TYPE_WEIGHT),
- MAP_WEIGHT4 = (1<<TYPE_WEIGHT4),
- MAP_CLOTHWEIGHT = (1<<TYPE_CLOTHWEIGHT),
- MAP_TEXTURE_INDEX = (1<<TYPE_TEXTURE_INDEX),
- };
-
-protected:
- friend class LLRender;
-
- ~LLVertexBuffer(); // use unref()
-
- void setupVertexBuffer();
-
- void genBuffer(U32 size);
- void genIndices(U32 size);
- bool createGLBuffer(U32 size);
- bool createGLIndices(U32 size);
- void destroyGLBuffer();
- void destroyGLIndices();
- bool updateNumVerts(U32 nverts);
- bool updateNumIndices(U32 nindices);
-
-public:
- LLVertexBuffer(U32 typemask);
-
- // allocate buffer
- bool allocateBuffer(U32 nverts, U32 nindices);
-
- // map for data access (see also getFooStrider below)
- U8* mapVertexBuffer(AttributeType type, U32 index, S32 count = -1);
- U8* mapIndexBuffer(U32 index, S32 count = -1);
- void unmapBuffer();
-
- // set for rendering
- // assumes (and will assert on) the following:
- // - this buffer has no pending unampBuffer call
- // - a shader is currently bound
- // - This buffer has sufficient attributes within it to satisfy the needs of the currently bound shader
- void setBuffer();
-
- // Only call each getVertexPointer, etc, once before calling unmapBuffer()
- // call unmapBuffer() after calls to getXXXStrider() before any cals to setBuffer()
- // example:
- // vb->getVertexBuffer(verts);
- // vb->getNormalStrider(norms);
- // setVertsNorms(verts, norms);
- // vb->unmapBuffer();
- bool getVertexStrider(LLStrider<LLVector3>& strider, U32 index=0, S32 count = -1);
- bool getVertexStrider(LLStrider<LLVector4a>& strider, U32 index=0, S32 count = -1);
- bool getIndexStrider(LLStrider<U16>& strider, U32 index=0, S32 count = -1);
- bool getTexCoord0Strider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
- bool getTexCoord1Strider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
- bool getTexCoord2Strider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
- bool getNormalStrider(LLStrider<LLVector3>& strider, U32 index=0, S32 count = -1);
- bool getTangentStrider(LLStrider<LLVector3>& strider, U32 index=0, S32 count = -1);
- bool getTangentStrider(LLStrider<LLVector4a>& strider, U32 index=0, S32 count = -1);
- bool getColorStrider(LLStrider<LLColor4U>& strider, U32 index=0, S32 count = -1);
- bool getEmissiveStrider(LLStrider<LLColor4U>& strider, U32 index=0, S32 count = -1);
- bool getWeightStrider(LLStrider<F32>& strider, U32 index=0, S32 count = -1);
- bool getWeight4Strider(LLStrider<LLVector4>& strider, U32 index=0, S32 count = -1);
- bool getClothWeightStrider(LLStrider<LLVector4>& strider, U32 index=0, S32 count = -1);
- bool getBasecolorTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
- bool getNormalTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
- bool getMetallicRoughnessTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
- bool getEmissiveTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
-
- void setPositionData(const LLVector4a* data);
- void setTexCoordData(const LLVector2* data);
- void setColorData(const LLColor4U* data);
-
-
- U32 getNumVerts() const { return mNumVerts; }
- U32 getNumIndices() const { return mNumIndices; }
-
- U32 getTypeMask() const { return mTypeMask; }
- bool hasDataType(AttributeType type) const { return ((1 << type) & getTypeMask()); }
- U32 getSize() const { return mSize; }
- U32 getIndicesSize() const { return mIndicesSize; }
- U8* getMappedData() const { return mMappedData; }
- U8* getMappedIndices() const { return mMappedIndexData; }
- U32 getOffset(AttributeType type) const { return mOffsets[type]; }
-
- // these functions assume (and assert on) the current VBO being bound
- // Detailed error checking can be enabled by setting gDebugGL to true
- void draw(U32 mode, U32 count, U32 indices_offset) const;
- void drawArrays(U32 mode, U32 offset, U32 count) const;
- void drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const;
-
- //for debugging, validate data in given range is valid
- bool validateRange(U32 start, U32 end, U32 count, U32 offset) const;
-
- #ifdef LL_PROFILER_ENABLE_RENDER_DOC
- void setLabel(const char* label);
- #endif
-
-
-protected:
- U32 mGLBuffer = 0; // GL VBO handle
- U32 mGLIndices = 0; // GL IBO handle
- U32 mNumVerts = 0; // Number of vertices allocated
- U32 mNumIndices = 0; // Number of indices allocated
- U32 mOffsets[TYPE_MAX]; // byte offsets into mMappedData of each attribute
-
- U8* mMappedData = nullptr; // pointer to currently mapped data (NULL if unmapped)
- U8* mMappedIndexData = nullptr; // pointer to currently mapped indices (NULL if unmapped)
-
- U32 mTypeMask = 0; // bitmask of present vertex attributes
-
- U32 mSize = 0; // size in bytes of mMappedData
- U32 mIndicesSize = 0; // size in bytes of mMappedIndexData
-
- std::vector<MappedRegion> mMappedVertexRegions; // list of mMappedData byte ranges that must be sent to GL
- std::vector<MappedRegion> mMappedIndexRegions; // list of mMappedIndexData byte ranges that must be sent to GL
-
-private:
- // DEPRECATED
- // These function signatures are deprecated, but for some reason
- // there are classes in an external package that depend on LLVertexBuffer
-
- // TODO: move these classes into viewer repository
- friend class LLNavShapeVBOManager;
- friend class LLNavMeshVBOManager;
-
- LLVertexBuffer(U32 typemask, U32 usage)
- : LLVertexBuffer(typemask)
- {}
-
- bool allocateBuffer(S32 nverts, S32 nindices, bool create) { return allocateBuffer(nverts, nindices); }
-
-public:
-
- static U64 getBytesAllocated();
- static const U32 sTypeSize[TYPE_MAX];
- static const U32 sGLMode[LLRender::NUM_MODES];
- static U32 sGLRenderBuffer;
- static U32 sGLRenderIndices;
- static U32 sLastMask;
- static U32 sVertexCount;
-};
-
-#ifdef LL_PROFILER_ENABLE_RENDER_DOC
-#define LL_LABEL_VERTEX_BUFFER(buf, name) buf->setLabel(name)
-#else
-#define LL_LABEL_VERTEX_BUFFER(buf, name)
-#endif
-
-
-#endif // LL_LLVERTEXBUFFER_H
+/**
+ * @file llvertexbuffer.h
+ * @brief LLVertexBuffer wrapper for OpengGL vertex buffer objects
+ *
+ * $LicenseInfo:firstyear=2003&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_LLVERTEXBUFFER_H
+#define LL_LLVERTEXBUFFER_H
+
+#include "llgl.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "v4coloru.h"
+#include "llstrider.h"
+#include "llrender.h"
+#include "lltrace.h"
+#include <set>
+#include <vector>
+#include <list>
+
+#define LL_MAX_VERTEX_ATTRIB_LOCATION 64
+
+//============================================================================
+// NOTES
+// Threading:
+// All constructors should take an 'create' paramater which should only be
+// 'true' when called from the main thread. Otherwise createGLBuffer() will
+// be called as soon as getVertexPointer(), etc is called (which MUST ONLY be
+// called from the main (i.e OpenGL) thread)
+
+
+//============================================================================
+// base class
+class LLPrivateMemoryPool;
+class LLVertexBuffer final : public LLRefCount
+{
+public:
+ struct MappedRegion
+ {
+ U32 mStart;
+ U32 mEnd;
+ };
+
+ LLVertexBuffer(const LLVertexBuffer& rhs)
+ {
+ *this = rhs;
+ }
+
+ const LLVertexBuffer& operator=(const LLVertexBuffer& rhs)
+ {
+ LL_ERRS() << "Illegal operation!" << LL_ENDL;
+ return *this;
+ }
+
+ static void initClass(LLWindow* window);
+ static void cleanupClass();
+ static void setupClientArrays(U32 data_mask);
+ static void drawArrays(U32 mode, const std::vector<LLVector3>& pos);
+ static void drawElements(U32 mode, const LLVector4a* pos, const LLVector2* tc, U32 num_indices, const U16* indicesp);
+
+ static void unbind(); //unbind any bound vertex buffer
+
+ //get the size of a vertex with the given typemask
+ static U32 calcVertexSize(const U32& typemask);
+
+ //get the size of a buffer with the given typemask and vertex count
+ //fill offsets with the offset of each vertex component array into the buffer
+ // indexed by the following enum
+ static U32 calcOffsets(const U32& typemask, U32* offsets, U32 num_vertices);
+
+ //WARNING -- when updating these enums you MUST
+ // 1 - update LLVertexBuffer::sTypeSize
+ // 2 - update LLVertexBuffer::vb_type_name
+ // 3 - add a strider accessor
+ // 4 - modify LLVertexBuffer::setupVertexBuffer
+ // 6 - modify LLViewerShaderMgr::mReservedAttribs
+
+ // clang-format off
+ enum AttributeType { // Shader attribute name, set in LLShaderMgr::initAttribsAndUniforms()
+ TYPE_VERTEX = 0, // "position"
+ TYPE_NORMAL, // "normal"
+ TYPE_TEXCOORD0, // "texcoord0"
+ TYPE_TEXCOORD1, // "texcoord1"
+ TYPE_TEXCOORD2, // "texcoord2"
+ TYPE_TEXCOORD3, // "texcoord3"
+ TYPE_COLOR, // "diffuse_color"
+ TYPE_EMISSIVE, // "emissive"
+ TYPE_TANGENT, // "tangent"
+ TYPE_WEIGHT, // "weight"
+ TYPE_WEIGHT4, // "weight4"
+ TYPE_CLOTHWEIGHT, // "clothing"
+ TYPE_TEXTURE_INDEX, // "texture_index"
+ TYPE_MAX, // TYPE_MAX is the size/boundary marker for attributes that go in the vertex buffer
+ TYPE_INDEX, // TYPE_INDEX is beyond _MAX because it lives in a separate (index) buffer
+ };
+ // clang-format on
+
+ enum {
+ MAP_VERTEX = (1<<TYPE_VERTEX),
+ MAP_NORMAL = (1<<TYPE_NORMAL),
+ 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),
+ MAP_EMISSIVE = (1<<TYPE_EMISSIVE),
+ MAP_TANGENT = (1<<TYPE_TANGENT),
+ MAP_WEIGHT = (1<<TYPE_WEIGHT),
+ MAP_WEIGHT4 = (1<<TYPE_WEIGHT4),
+ MAP_CLOTHWEIGHT = (1<<TYPE_CLOTHWEIGHT),
+ MAP_TEXTURE_INDEX = (1<<TYPE_TEXTURE_INDEX),
+ };
+
+protected:
+ friend class LLRender;
+
+ ~LLVertexBuffer(); // use unref()
+
+ void setupVertexBuffer();
+
+ void genBuffer(U32 size);
+ void genIndices(U32 size);
+ bool createGLBuffer(U32 size);
+ bool createGLIndices(U32 size);
+ void destroyGLBuffer();
+ void destroyGLIndices();
+ bool updateNumVerts(U32 nverts);
+ bool updateNumIndices(U32 nindices);
+
+public:
+ LLVertexBuffer(U32 typemask);
+
+ // allocate buffer
+ bool allocateBuffer(U32 nverts, U32 nindices);
+
+ // map for data access (see also getFooStrider below)
+ U8* mapVertexBuffer(AttributeType type, U32 index, S32 count = -1);
+ U8* mapIndexBuffer(U32 index, S32 count = -1);
+ void unmapBuffer();
+
+ // set for rendering
+ // assumes (and will assert on) the following:
+ // - this buffer has no pending unampBuffer call
+ // - a shader is currently bound
+ // - This buffer has sufficient attributes within it to satisfy the needs of the currently bound shader
+ void setBuffer();
+
+ // Only call each getVertexPointer, etc, once before calling unmapBuffer()
+ // call unmapBuffer() after calls to getXXXStrider() before any cals to setBuffer()
+ // example:
+ // vb->getVertexBuffer(verts);
+ // vb->getNormalStrider(norms);
+ // setVertsNorms(verts, norms);
+ // vb->unmapBuffer();
+ bool getVertexStrider(LLStrider<LLVector3>& strider, U32 index=0, S32 count = -1);
+ bool getVertexStrider(LLStrider<LLVector4a>& strider, U32 index=0, S32 count = -1);
+ bool getIndexStrider(LLStrider<U16>& strider, U32 index=0, S32 count = -1);
+ bool getTexCoord0Strider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
+ bool getTexCoord1Strider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
+ bool getTexCoord2Strider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
+ bool getNormalStrider(LLStrider<LLVector3>& strider, U32 index=0, S32 count = -1);
+ bool getTangentStrider(LLStrider<LLVector3>& strider, U32 index=0, S32 count = -1);
+ bool getTangentStrider(LLStrider<LLVector4a>& strider, U32 index=0, S32 count = -1);
+ bool getColorStrider(LLStrider<LLColor4U>& strider, U32 index=0, S32 count = -1);
+ bool getEmissiveStrider(LLStrider<LLColor4U>& strider, U32 index=0, S32 count = -1);
+ bool getWeightStrider(LLStrider<F32>& strider, U32 index=0, S32 count = -1);
+ bool getWeight4Strider(LLStrider<LLVector4>& strider, U32 index=0, S32 count = -1);
+ bool getClothWeightStrider(LLStrider<LLVector4>& strider, U32 index=0, S32 count = -1);
+ bool getBasecolorTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
+ bool getNormalTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
+ bool getMetallicRoughnessTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
+ bool getEmissiveTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
+
+ void setPositionData(const LLVector4a* data);
+ void setTexCoordData(const LLVector2* data);
+ void setColorData(const LLColor4U* data);
+
+
+ U32 getNumVerts() const { return mNumVerts; }
+ U32 getNumIndices() const { return mNumIndices; }
+
+ U32 getTypeMask() const { return mTypeMask; }
+ bool hasDataType(AttributeType type) const { return ((1 << type) & getTypeMask()); }
+ U32 getSize() const { return mSize; }
+ U32 getIndicesSize() const { return mIndicesSize; }
+ U8* getMappedData() const { return mMappedData; }
+ U8* getMappedIndices() const { return mMappedIndexData; }
+ U32 getOffset(AttributeType type) const { return mOffsets[type]; }
+
+ // these functions assume (and assert on) the current VBO being bound
+ // Detailed error checking can be enabled by setting gDebugGL to true
+ void draw(U32 mode, U32 count, U32 indices_offset) const;
+ void drawArrays(U32 mode, U32 offset, U32 count) const;
+ void drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const;
+
+ //for debugging, validate data in given range is valid
+ bool validateRange(U32 start, U32 end, U32 count, U32 offset) const;
+
+ #ifdef LL_PROFILER_ENABLE_RENDER_DOC
+ void setLabel(const char* label);
+ #endif
+
+
+protected:
+ U32 mGLBuffer = 0; // GL VBO handle
+ U32 mGLIndices = 0; // GL IBO handle
+ U32 mNumVerts = 0; // Number of vertices allocated
+ U32 mNumIndices = 0; // Number of indices allocated
+ U32 mOffsets[TYPE_MAX]; // byte offsets into mMappedData of each attribute
+
+ U8* mMappedData = nullptr; // pointer to currently mapped data (NULL if unmapped)
+ U8* mMappedIndexData = nullptr; // pointer to currently mapped indices (NULL if unmapped)
+
+ U32 mTypeMask = 0; // bitmask of present vertex attributes
+
+ U32 mSize = 0; // size in bytes of mMappedData
+ U32 mIndicesSize = 0; // size in bytes of mMappedIndexData
+
+ std::vector<MappedRegion> mMappedVertexRegions; // list of mMappedData byte ranges that must be sent to GL
+ std::vector<MappedRegion> mMappedIndexRegions; // list of mMappedIndexData byte ranges that must be sent to GL
+
+private:
+ // DEPRECATED
+ // These function signatures are deprecated, but for some reason
+ // there are classes in an external package that depend on LLVertexBuffer
+
+ // TODO: move these classes into viewer repository
+ friend class LLNavShapeVBOManager;
+ friend class LLNavMeshVBOManager;
+
+ LLVertexBuffer(U32 typemask, U32 usage)
+ : LLVertexBuffer(typemask)
+ {}
+
+ bool allocateBuffer(S32 nverts, S32 nindices, bool create) { return allocateBuffer(nverts, nindices); }
+
+public:
+
+ static U64 getBytesAllocated();
+ static const U32 sTypeSize[TYPE_MAX];
+ static const U32 sGLMode[LLRender::NUM_MODES];
+ static U32 sGLRenderBuffer;
+ static U32 sGLRenderIndices;
+ static U32 sLastMask;
+ static U32 sVertexCount;
+};
+
+#ifdef LL_PROFILER_ENABLE_RENDER_DOC
+#define LL_LABEL_VERTEX_BUFFER(buf, name) buf->setLabel(name)
+#else
+#define LL_LABEL_VERTEX_BUFFER(buf, name)
+#endif
+
+
+#endif // LL_LLVERTEXBUFFER_H