diff options
Diffstat (limited to 'indra/llrender')
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 |