From 74c8b028d42a8c5b080bb861e427f38cedd4ad7c Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Fri, 15 Dec 2023 18:26:14 +0100 Subject: SL-20743 Use LLMutex in LLImageBase for internal data thread-safety --- indra/llrender/llcubemap.cpp | 3 +++ indra/llrender/llfontfreetype.cpp | 1 + indra/llrender/llimagegl.cpp | 2 ++ 3 files changed, 6 insertions(+) (limited to 'indra/llrender') diff --git a/indra/llrender/llcubemap.cpp b/indra/llrender/llcubemap.cpp index 254288a86e..26bd925f03 100644 --- a/indra/llrender/llcubemap.cpp +++ b/indra/llrender/llcubemap.cpp @@ -111,6 +111,9 @@ void LLCubeMap::initRawData(const std::vector >& rawimages // 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(); diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp index e964d1586f..11a379d70f 100644 --- a/indra/llrender/llfontfreetype.cpp +++ b/indra/llrender/llfontfreetype.cpp @@ -669,6 +669,7 @@ U8 LLFontFreetype::getStyle() const void LLFontFreetype::setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride) const { LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_num); + LLImageDataLock lock(image_raw); llassert(!mIsFallback); llassert(image_raw && (image_raw->getComponents() == 2)); diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index c6fd824c4e..bbedcf360c 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -1887,6 +1887,8 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre } //----------------------------------------------------------------------------------------------- + LLImageDataLock lock(imageraw); + if (is_compressed) { LLGLint glbytes; -- cgit v1.2.3 From da967da9c7aa0d88f478d476e8bb059ba79ca818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Thu, 11 Jan 2024 11:32:15 +0100 Subject: Rename OS X to macOS, mostly in comments We only support 10.13+ now, and it's been called macOS since 10.12. References in code to older versions are unchanged. --- indra/llrender/llfontgl.cpp | 2 +- indra/llrender/llimagegl.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 15fddbc99f..484fa9d82c 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -1110,7 +1110,7 @@ LLFontGL* LLFontGL::getFontDefault() std::string LLFontGL::getFontPathSystem() { #if LL_DARWIN - // HACK for Mac OS X + // HACK for macOS return "/System/Library/Fonts/"; #elif LL_WINDOWS diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index bbedcf360c..6315b2cd7c 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -56,7 +56,7 @@ const F32 MIN_TEXTURE_LIFETIME = 10.f; U32 wpo2(U32 i); -// texture memory accounting (for OS X) +// texture memory accounting (for macOS) static LLMutex sTexMemMutex; static std::unordered_map sTextureAllocs; static U64 sTextureBytes = 0; -- cgit v1.2.3 From 7316441f22e516db17f27a6102e012713c2b0ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Fri, 9 Feb 2024 04:25:50 +0100 Subject: llrender: BOOL (int) to real bool --- indra/llrender/llcubemaparray.cpp | 2 +- indra/llrender/llcubemaparray.h | 2 +- indra/llrender/llfontbitmapcache.cpp | 4 ++-- indra/llrender/llfontbitmapcache.h | 2 +- indra/llrender/llfontregistry.cpp | 4 ++-- indra/llrender/llglslshader.cpp | 26 +++++++++++++------------- indra/llrender/llglslshader.h | 6 +++--- indra/llrender/llrender2dutils.cpp | 8 ++++---- indra/llrender/llrender2dutils.h | 2 +- 9 files changed, 28 insertions(+), 28 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llcubemaparray.cpp b/indra/llrender/llcubemaparray.cpp index 1debd33953..ed0ad07dc0 100644 --- a/indra/llrender/llcubemaparray.cpp +++ b/indra/llrender/llcubemaparray.cpp @@ -107,7 +107,7 @@ LLCubeMapArray::~LLCubeMapArray() { } -void LLCubeMapArray::allocate(U32 resolution, U32 components, U32 count, BOOL use_mips) +void LLCubeMapArray::allocate(U32 resolution, U32 components, U32 count, bool use_mips) { U32 texname = 0; mWidth = resolution; diff --git a/indra/llrender/llcubemaparray.h b/indra/llrender/llcubemaparray.h index 6c3f7dc890..1b0fa626c4 100644 --- a/indra/llrender/llcubemaparray.h +++ b/indra/llrender/llcubemaparray.h @@ -52,7 +52,7 @@ public: // 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 allocate(U32 res, U32 components, U32 count, bool use_mips = true); void bind(S32 stage); void unbind(); diff --git a/indra/llrender/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp index c71e24c83a..004888f8bc 100644 --- a/indra/llrender/llfontbitmapcache.cpp +++ b/indra/llrender/llfontbitmapcache.cpp @@ -73,7 +73,7 @@ LLImageGL *LLFontBitmapCache::getImageGL(U32 bitmap_num) const } -BOOL LLFontBitmapCache::nextOpenPos(S32 width, S32 &pos_x, S32 &pos_y, S32& bitmap_num) +bool LLFontBitmapCache::nextOpenPos(S32 width, S32 &pos_x, S32 &pos_y, S32& bitmap_num) { if ((mBitmapNum<0) || (mCurrentOffsetX + width + 1) > mBitmapWidth) { @@ -138,7 +138,7 @@ BOOL LLFontBitmapCache::nextOpenPos(S32 width, S32 &pos_x, S32 &pos_y, S32& bitm mCurrentOffsetX += width + 1; - return TRUE; + return true; } void LLFontBitmapCache::destroyGL() diff --git a/indra/llrender/llfontbitmapcache.h b/indra/llrender/llfontbitmapcache.h index 7de3a6b56f..4bbd464cea 100644 --- a/indra/llrender/llfontbitmapcache.h +++ b/indra/llrender/llfontbitmapcache.h @@ -45,7 +45,7 @@ public: void reset(); - BOOL nextOpenPos(S32 width, S32 &posX, S32 &posY, S32 &bitmapNum); + bool nextOpenPos(S32 width, S32 &posX, S32 &posY, S32 &bitmapNum); void destroyGL(); diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index 9750bd4fa1..032396ca33 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -457,7 +457,7 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc) // Snarf all fonts we can into fontlist. First will get pulled // off the list and become the "head" font, set to non-fallback. // Rest will consitute the fallback list. - BOOL is_first_found = TRUE; + bool is_first_found = true; std::string local_path = LLFontGL::getFontPathLocal(); std::string sys_path = LLFontGL::getFontPathSystem(); @@ -481,7 +481,7 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc) bool is_ft_collection = (std::find(ft_collection_list.begin(), ft_collection_list.end(), *file_name_it) != ft_collection_list.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; + 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; diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index ccfb8f69be..65c44ed04c 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -81,7 +81,7 @@ const std::string gShaderConstsVal[LLGLSLShader::NUM_SHADER_CONSTS] = }; -BOOL shouldChange(const LLVector4& v1, const LLVector4& v2) +bool shouldChange(const LLVector4& v1, const LLVector4& v2) { return v1 != v2; } @@ -381,7 +381,7 @@ void LLGLSLShader::unloadInternal() stop_glerror(); } -BOOL LLGLSLShader::createShader(std::vector* attributes, +bool LLGLSLShader::createShader(std::vector* attributes, std::vector* uniforms, U32 varying_count, const char** varyings) @@ -415,10 +415,10 @@ BOOL LLGLSLShader::createShader(std::vector* attributes, // 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; + return false; } - BOOL success = TRUE; + bool success = true; mUsingBinaryProgram = LLShaderMgr::instance()->loadCachedProgramBinary(this); @@ -440,7 +440,7 @@ BOOL LLGLSLShader::createShader(std::vector* attributes, } else { - success = FALSE; + success = false; } } } @@ -449,7 +449,7 @@ BOOL LLGLSLShader::createShader(std::vector* attributes, if (!LLShaderMgr::instance()->attachShaderFeatures(this)) { unloadInternal(); - return FALSE; + return false; } // Map attributes and uniforms if (success) @@ -529,7 +529,7 @@ void dumpAttachObject(const char* func_name, GLuint program_object, const std::s } #endif // DEBUG_SHADER_INCLUDES -BOOL LLGLSLShader::attachVertexObject(std::string object_path) +bool LLGLSLShader::attachVertexObject(std::string object_path) { if (LLShaderMgr::instance()->mVertexShaderObjects.count(object_path) > 0) { @@ -539,19 +539,19 @@ BOOL LLGLSLShader::attachVertexObject(std::string object_path) dumpAttachObject("attachVertexObject", mProgramObject, object_path); #endif // DEBUG_SHADER_INCLUDES stop_glerror(); - return TRUE; + return true; } else { LL_SHADER_LOADING_WARNS() << "Attempting to attach shader object: '" << object_path << "' that hasn't been compiled." << LL_ENDL; - return FALSE; + return false; } } -BOOL LLGLSLShader::attachFragmentObject(std::string object_path) +bool LLGLSLShader::attachFragmentObject(std::string object_path) { if(mUsingBinaryProgram) - return TRUE; + return true; if (LLShaderMgr::instance()->mFragmentShaderObjects.count(object_path) > 0) { @@ -561,12 +561,12 @@ BOOL LLGLSLShader::attachFragmentObject(std::string object_path) dumpAttachObject("attachFragmentObject", mProgramObject, object_path); #endif // DEBUG_SHADER_INCLUDES stop_glerror(); - return TRUE; + return true; } else { LL_SHADER_LOADING_WARNS() << "Attempting to attach shader object: '" << object_path << "' that hasn't been compiled." << LL_ENDL; - return FALSE; + return false; } } diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 43d095f73a..af9472e2ea 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -174,12 +174,12 @@ public: // 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* attributes, + bool createShader(std::vector* attributes, std::vector* uniforms, U32 varying_count = 0, const char** varyings = NULL); - BOOL attachFragmentObject(std::string object); - BOOL attachVertexObject(std::string object); + 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* attributes); diff --git a/indra/llrender/llrender2dutils.cpp b/indra/llrender/llrender2dutils.cpp index 52869406d2..df1169b315 100644 --- a/indra/llrender/llrender2dutils.cpp +++ b/indra/llrender/llrender2dutils.cpp @@ -51,11 +51,11 @@ 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) +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; + if (x < left || right < x) return false; + if (y < bottom || top < y) return false; + return true; } diff --git a/indra/llrender/llrender2dutils.h b/indra/llrender/llrender2dutils.h index 135738c3ba..298b357a38 100644 --- a/indra/llrender/llrender2dutils.h +++ b/indra/llrender/llrender2dutils.h @@ -43,7 +43,7 @@ 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); +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); -- cgit v1.2.3 From 088f2f4f6545ebc2ee01945938a40ae5c87ad27a Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 17 Feb 2024 00:51:13 +0100 Subject: More BOOL to bool replacements primarily in llappearance and llxml --- indra/llrender/llfontregistry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index 032396ca33..ae27fcbe48 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -277,7 +277,7 @@ bool font_desc_init_from_xml(LLXMLNodePtr node, LLFontDescriptor& desc) if (child->hasAttribute("load_collection")) { - BOOL col = FALSE; + bool col = false; child->getAttributeBOOL("load_collection", col); if (col) { -- cgit v1.2.3 From f1c97f4057833220a2e9ac045d701208e30457d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Sun, 18 Feb 2024 16:41:22 +0100 Subject: misc: BOOL to bool --- indra/llrender/llgl.h | 2 +- indra/llrender/llimagegl.cpp | 4 ++-- indra/llrender/llimagegl.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 4b0fbc0466..2ded1d1906 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -45,7 +45,7 @@ #include "llglheaders.h" #include "glh/glh_linear.h" -extern BOOL gDebugGL; +extern bool gDebugGL; extern BOOL gDebugSession; extern BOOL gDebugGLSession; extern llofstream gFailLog; diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 6315b2cd7c..78c4c90663 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -2072,9 +2072,9 @@ S64 LLImageGL::getMipBytes(S32 discard_level) const return res; } -BOOL LLImageGL::isJustBound() const +bool LLImageGL::isJustBound() const { - return (BOOL)(sLastFrameTime - mLastBindTime < 0.5f); + return sLastFrameTime - mLastBindTime < 0.5f; } BOOL LLImageGL::getBoundRecently() const diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index a9a6b93cb3..1b77fd0964 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -148,7 +148,7 @@ public: S64 getBytes(S32 discard_level = -1) const; S64 getMipBytes(S32 discard_level = -1) const; BOOL getBoundRecently() const; - BOOL isJustBound() const; + bool isJustBound() const; BOOL getHasExplicitFormat() const { return mHasExplicitFormat; } LLGLenum getPrimaryFormat() const { return mFormatPrimary; } LLGLenum getFormatType() const { return mFormatType; } -- cgit v1.2.3 From b2c271367296744fbbe2262e55d0ea4f8f5ccdc9 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 20 Feb 2024 00:50:39 +0100 Subject: Convert BOOL to bool in llrender --- indra/llrender/llcubemap.cpp | 10 +- indra/llrender/llcubemaparray.h | 2 +- indra/llrender/llfontbitmapcache.cpp | 4 +- indra/llrender/llfontfreetype.cpp | 16 +-- indra/llrender/llfontfreetype.h | 6 +- indra/llrender/llfontgl.cpp | 40 +++---- indra/llrender/llfontgl.h | 16 +-- indra/llrender/llgl.cpp | 56 +++++----- indra/llrender/llgl.h | 44 ++++---- indra/llrender/llglslshader.cpp | 20 ++-- indra/llrender/llglslshader.h | 8 +- indra/llrender/llgltexture.cpp | 42 +++---- indra/llrender/llgltexture.h | 36 +++--- indra/llrender/llimagegl.cpp | 190 ++++++++++++++++---------------- indra/llrender/llimagegl.h | 86 +++++++-------- indra/llrender/llpostprocess.cpp | 6 +- indra/llrender/llrender.cpp | 2 +- indra/llrender/llrender2dutils.cpp | 22 ++-- indra/llrender/llrender2dutils.h | 28 ++--- indra/llrender/llshadermgr.cpp | 86 +++++++-------- indra/llrender/llshadermgr.h | 8 +- indra/llrender/lltexturemanagerbridge.h | 4 +- indra/llrender/lluiimage.inl | 4 +- indra/llrender/llvertexbuffer.h | 2 +- 24 files changed, 369 insertions(+), 369 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llcubemap.cpp b/indra/llrender/llcubemap.cpp index 26bd925f03..3fe0ff8041 100644 --- a/indra/llrender/llcubemap.cpp +++ b/indra/llrender/llcubemap.cpp @@ -78,7 +78,7 @@ void LLCubeMap::initGL() for (int i = 0; i < 6; i++) { - mImages[i] = new LLImageGL(RESOLUTION, RESOLUTION, 4, FALSE); + mImages[i] = new LLImageGL(RESOLUTION, RESOLUTION, 4, false); #if USE_SRGB_DECODE if (mIssRGB) { mImages[i]->setExplicitFormat(GL_SRGB8_ALPHA8, GL_RGBA); @@ -176,7 +176,7 @@ void LLCubeMap::initReflectionMap(U32 resolution, U32 components) LLImageGL::generateTextures(1, &texname); - mImages[0] = new LLImageGL(resolution, resolution, components, TRUE); + 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); @@ -200,7 +200,7 @@ void LLCubeMap::initEnvironmentMap(const std::vector >& ra llassert(rawimages[i]->getHeight() == resolution); llassert(rawimages[i]->getComponents() == components); - mImages[i] = new LLImageGL(resolution, resolution, components, TRUE); + 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); @@ -224,8 +224,8 @@ void LLCubeMap::generateMipMaps() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - mImages[0]->setUseMipMaps(TRUE); - mImages[0]->setHasMipMaps(TRUE); + mImages[0]->setUseMipMaps(true); + mImages[0]->setHasMipMaps(true); enableTexture(0); bind(); mImages[0]->setFilteringOption(LLTexUnit::TFO_BILINEAR); diff --git a/indra/llrender/llcubemaparray.h b/indra/llrender/llcubemaparray.h index 1b0fa626c4..fb4508187c 100644 --- a/indra/llrender/llcubemaparray.h +++ b/indra/llrender/llcubemaparray.h @@ -51,7 +51,7 @@ public: // 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 + // 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(); diff --git a/indra/llrender/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp index 004888f8bc..3f6d85b542 100644 --- a/indra/llrender/llfontbitmapcache.cpp +++ b/indra/llrender/llfontbitmapcache.cpp @@ -87,7 +87,7 @@ bool LLFontBitmapCache::nextOpenPos(S32 width, S32 &pos_x, S32 &pos_y, S32& bitm LLImageRaw *image_raw = getImageRaw(mBitmapNum); // Make corresponding GL image. - mImageGLVec.push_back(new LLImageGL(FALSE)); + mImageGLVec.push_back(new LLImageGL(false)); LLImageGL *image_gl = getImageGL(mBitmapNum); S32 image_width = mMaxCharWidth * 20; @@ -122,7 +122,7 @@ bool LLFontBitmapCache::nextOpenPos(S32 width, S32 &pos_x, S32 &pos_y, S32& bitm // Attach corresponding GL texture. image_gl->createGLTexture(0, image_raw); gGL.getTexUnit(0)->bind(image_gl); - image_gl->setFilteringOption(LLTexUnit::TFO_POINT); // was setMipFilterNearest(TRUE, TRUE); + image_gl->setFilteringOption(LLTexUnit::TFO_POINT); // was setMipFilterNearest(true, true); } else { diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp index 11a379d70f..ffeed89454 100644 --- a/indra/llrender/llfontfreetype.cpp +++ b/indra/llrender/llfontfreetype.cpp @@ -112,7 +112,7 @@ LLFontFreetype::LLFontFreetype() pFileStream(NULL), pFtStream(NULL), #endif - mIsFallback(FALSE), + mIsFallback(false), mFTFace(NULL), mRenderGlyphCount(0), mAddGlyphCount(0), @@ -156,7 +156,7 @@ void ft_close_cb(FT_Stream stream) { } #endif -BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback, S32 face_n) +bool LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, bool is_fallback, S32 face_n) { // Don't leak face objects. This is also needed to deal with // changed font file names. @@ -181,7 +181,7 @@ BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v #ifdef LL_WINDOWS clearFontStreams(); #endif - return FALSE; + return false; } mIsFallback = is_fallback; @@ -201,7 +201,7 @@ BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v clearFontStreams(); #endif mFTFace = NULL; - return FALSE; + return false; } F32 y_max, y_min, x_max, x_min; @@ -248,7 +248,7 @@ BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v mStyle |= LLFontGL::ITALIC; } - return TRUE; + return true; } S32 LLFontFreetype::getNumFaces(const std::string& filename) @@ -414,7 +414,7 @@ F32 LLFontFreetype::getXKerning(const LLFontGlyphInfo* left_glyph_info, const LL return delta.x*(1.f/64.f); } -BOOL LLFontFreetype::hasGlyph(llwchar wch) const +bool LLFontFreetype::hasGlyph(llwchar wch) const { llassert(!mIsFallback); return(mCharGlyphInfoMap.find(wch) != mCharGlyphInfoMap.end()); @@ -423,7 +423,7 @@ BOOL LLFontFreetype::hasGlyph(llwchar wch) const LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch) const { if (mFTFace == NULL) - return FALSE; + return false; llassert(!mIsFallback); //LL_DEBUGS() << "Adding new glyph for " << wch << " to font" << LL_ENDL; @@ -529,7 +529,7 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l height, buffer_data, buffer_row_stride, - TRUE); + true); break; case 2: setSubImageLuminanceAlpha(pos_x, diff --git a/indra/llrender/llfontfreetype.h b/indra/llrender/llfontfreetype.h index f61f169987..543c6fbd8e 100644 --- a/indra/llrender/llfontfreetype.h +++ b/indra/llrender/llfontfreetype.h @@ -84,7 +84,7 @@ public: // is_fallback should be true for fallback fonts that aren't used // to render directly (Unicode backup, primarily) - BOOL loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback, S32 face_n = 0); + bool loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, bool is_fallback, S32 face_n = 0); S32 getNumFaces(const std::string& filename); @@ -151,7 +151,7 @@ public: private: void resetBitmapCache(); void setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride = 0) const; - BOOL hasGlyph(llwchar wch) const; // Has a glyph for this character + bool hasGlyph(llwchar wch) const; // Has a glyph for this character LLFontGlyphInfo* addGlyph(llwchar wch) const; // Add a new character to the font if necessary LLFontGlyphInfo* addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index) const; // Add a glyph from this font to the other (returns the glyph_index, 0 if not found) void renderGlyph(U32 glyph_index) const; @@ -173,7 +173,7 @@ private: LLFT_Stream *pFtStream; #endif - BOOL mIsFallback; + bool mIsFallback; font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars) typedef boost::unordered_map char_glyph_info_map_t; diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 484fa9d82c..4dfc6f2021 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -58,7 +58,7 @@ F32 LLFontGL::sVertDPI = 96.f; F32 LLFontGL::sHorizDPI = 96.f; F32 LLFontGL::sScaleX = 1.f; F32 LLFontGL::sScaleY = 1.f; -BOOL LLFontGL::sDisplayFont = TRUE ; +bool LLFontGL::sDisplayFont = true ; std::string LLFontGL::sAppDir; LLColor4 LLFontGL::sShadowColor(0.f, 0.f, 0.f, 1.f); @@ -89,7 +89,7 @@ void LLFontGL::destroyGL() mFontFreetype->destroyGL(); } -BOOL LLFontGL::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback, S32 face_n) +bool LLFontGL::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, bool is_fallback, S32 face_n) { if(mFontFreetype == reinterpret_cast(NULL)) { @@ -110,14 +110,14 @@ S32 LLFontGL::getNumFaces(const std::string& 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) const + ShadowType shadow, S32 max_chars, F32* right_x, bool use_ellipses) 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); } 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) const + ShadowType shadow, S32 max_chars, F32* right_x, bool use_ellipses) const { F32 x = rect.mLeft; F32 y = 0.f; @@ -143,7 +143,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRectf& rec S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, - ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const + ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, bool use_ellipses) const { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; @@ -255,7 +255,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; - BOOL draw_ellipses = FALSE; + bool draw_ellipses = false; if (use_ellipses) { // check for too long of a string @@ -265,7 +265,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons // 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; + draw_ellipses = true; } } @@ -409,7 +409,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons shadow, S32_MAX, max_pixels, right_x, - FALSE); + false); gGL.popUIMatrix(); } @@ -420,22 +420,22 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons S32 LLFontGL::render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const { - return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); + return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, false); } -S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const +S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, bool use_ellipses) const { return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses); } S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const { - return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); + return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, false); } S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow) const { - return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow, S32_MAX, S32_MAX, NULL, FALSE); + return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow, S32_MAX, S32_MAX, NULL, false); } // font metrics - override for LLFontFreetype that returns units of virtual pixels @@ -572,11 +572,11 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch llassert(max_pixels >= 0.f); llassert(max_chars >= 0); - BOOL clip = FALSE; + bool clip = false; F32 cur_x = 0; S32 start_of_last_word = 0; - BOOL in_word = FALSE; + bool in_word = false; // avoid S32 overflow when max_pixels == S32_MAX by staying in floating point F32 scaled_max_pixels = max_pixels * sScaleX; @@ -601,18 +601,18 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch { if(wch !=(0x00A0)) { - in_word = FALSE; + in_word = false; } } if (iswindividual(wch)) { if (iswpunct(wchars[i+1])) { - in_word=TRUE; + in_word=true; } else { - in_word=FALSE; + in_word=false; start_of_last_word = i; } } @@ -622,7 +622,7 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch start_of_last_word = i; if (!iswspace(wch)||!iswindividual(wch)) { - in_word = TRUE; + in_word = true; } } @@ -648,7 +648,7 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch // clip if current character runs past scaled_max_pixels (using width_padding) if (scaled_max_pixels < cur_x + width_padding) { - clip = TRUE; + clip = true; break; } @@ -746,7 +746,7 @@ S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_ } -S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 target_x, F32 max_pixels, S32 max_chars, BOOL round) const +S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 target_x, F32 max_pixels, S32 max_chars, bool round) const { if (!wchars || !wchars[0] || max_chars == 0) { diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 9bbf9ce0c4..551e704aca 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -87,7 +87,7 @@ public: void destroyGL(); - BOOL loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, const S32 components, BOOL is_fallback, S32 face_n = 0); + bool loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, const S32 components, bool is_fallback, S32 face_n = 0); S32 getNumFaces(const std::string& filename); @@ -98,7 +98,7 @@ public: U8 style = NORMAL, ShadowType shadow = NO_SHADOW, S32 max_chars = S32_MAX, F32* right_x=NULL, - BOOL use_ellipses = FALSE) const; + bool use_ellipses = false) const; S32 render(const LLWString &text, S32 begin_offset, const LLRectf& rect, @@ -107,7 +107,7 @@ public: U8 style = NORMAL, ShadowType shadow = NO_SHADOW, S32 max_chars = S32_MAX, F32* right_x=NULL, - BOOL use_ellipses = FALSE) const; + bool use_ellipses = false) const; S32 render(const LLWString &text, S32 begin_offset, F32 x, F32 y, @@ -116,12 +116,12 @@ public: U8 style = NORMAL, ShadowType shadow = NO_SHADOW, S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX, F32* right_x=NULL, - BOOL use_ellipses = FALSE) const; + bool use_ellipses = false) 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_pixels, F32* right_x, BOOL use_ellipses) const; + S32 renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, bool use_ellipses) const; S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const; S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style = NORMAL, ShadowType shadow = NO_SHADOW) const; @@ -156,7 +156,7 @@ public: 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; + 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; @@ -185,7 +185,7 @@ public: static std::string nameFromVAlign(LLFontGL::VAlign align); static LLFontGL::VAlign vAlignFromName(const std::string& name); - static void setFontDisplay(BOOL flag) { sDisplayFont = flag; } + static void setFontDisplay(bool flag) { sDisplayFont = flag; } static LLFontGL* getFontMonospace(); static LLFontGL* getFontSansSerifSmall(); @@ -213,7 +213,7 @@ public: static F32 sHorizDPI; static F32 sScaleX; static F32 sScaleY; - static BOOL sDisplayFont ; + static bool sDisplayFont ; static std::string sAppDir; // For loading fonts private: diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index 4c7c8e6f5c..b6cbef142c 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -59,12 +59,12 @@ #endif -BOOL gDebugSession = FALSE; -BOOL gDebugGLSession = FALSE; -BOOL gClothRipple = FALSE; -BOOL gHeadlessClient = FALSE; -BOOL gNonInteractive = FALSE; -BOOL gGLActive = FALSE; +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"); @@ -971,21 +971,21 @@ PFNGLPOLYGONOFFSETCLAMPPROC glPolygonOffsetClamp = nullptr; LLGLManager gGLManager; LLGLManager::LLGLManager() : - mInited(FALSE), - mIsDisabled(FALSE), + mInited(false), + mIsDisabled(false), mMaxSamples(0), mNumTextureImageUnits(1), mMaxSampleMaskWords(0), mMaxColorTextureSamples(0), mMaxDepthTextureSamples(0), mMaxIntegerSamples(0), - mIsAMD(FALSE), - mIsNVIDIA(FALSE), - mIsIntel(FALSE), + mIsAMD(false), + mIsNVIDIA(false), + mIsIntel(false), #if LL_DARWIN - mIsMobileGF(FALSE), + mIsMobileGF(false), #endif - mHasRequirements(TRUE), + mHasRequirements(true), mDriverVersionMajor(1), mDriverVersionMinor(0), mDriverVersionRelease(0), @@ -1135,12 +1135,12 @@ bool LLGLManager::initGL() { mGLVendorShort = "AMD"; // *TODO: Fix this? - mIsAMD = TRUE; + mIsAMD = true; } else if (mGLVendor.find("NVIDIA ") != std::string::npos) { mGLVendorShort = "NVIDIA"; - mIsNVIDIA = TRUE; + mIsNVIDIA = true; } else if (mGLVendor.find("INTEL") != std::string::npos #if LL_LINUX @@ -1151,7 +1151,7 @@ bool LLGLManager::initGL() ) { mGLVendorShort = "INTEL"; - mIsIntel = TRUE; + mIsIntel = true; } else { @@ -1360,12 +1360,12 @@ void LLGLManager::shutdownGL() { glFinish(); stop_glerror(); - mInited = FALSE; + 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. +// presence of vertex skinning/blending or vertex programs will set these to false by default. void LLGLManager::initExtensions() { @@ -1397,7 +1397,7 @@ void LLGLManager::initExtensions() glGetIntegerv(GL_MAX_ELEMENTS_INDICES, (GLint*) &mGLMaxIndexRange); glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint*) &mGLMaxTextureSize); - mInited = TRUE; + mInited = true; #if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS LL_DEBUGS("RenderInit") << "GL Probe: Getting symbols" << LL_ENDL; @@ -2285,10 +2285,10 @@ void do_assert_glerror() // Create or update texture to be used with this data GLenum error; error = glGetError(); - BOOL quit = FALSE; + bool quit = false; if (LL_UNLIKELY(error)) { - quit = TRUE; + quit = true; GLubyte const * gl_error_msg = gluErrorString(error); if (NULL != gl_error_msg) { @@ -2408,7 +2408,7 @@ void LLGLState::dumpStates() for (boost::unordered_map::iterator iter = sStateMap.begin(); iter != sStateMap.end(); ++iter) { - LL_INFOS("RenderState") << llformat(" 0x%04x : %s",(S32)iter->first,iter->second?"TRUE":"FALSE") << LL_ENDL; + LL_INFOS("RenderState") << llformat(" 0x%04x : %s",(S32)iter->first,iter->second?"true":"false") << LL_ENDL; } } @@ -2451,7 +2451,7 @@ void LLGLState::checkStates(GLboolean writeAlpha) /////////////////////////////////////////////////////////////////////// LLGLState::LLGLState(LLGLenum state, S32 enabled) : - mState(state), mWasEnabled(FALSE), mIsEnabled(FALSE) + mState(state), mWasEnabled(false), mIsEnabled(false) { LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE; @@ -2470,15 +2470,15 @@ void LLGLState::setEnabled(S32 enabled) } if (enabled == CURRENT_STATE) { - enabled = sStateMap[mState] == GL_TRUE ? TRUE : FALSE; + enabled = sStateMap[mState] == GL_TRUE ? ENABLED_STATE : DISABLED_STATE; } - else if (enabled == TRUE && sStateMap[mState] != GL_TRUE) + else if (enabled == ENABLED_STATE && sStateMap[mState] != GL_TRUE) { gGL.flush(); glEnable(mState); sStateMap[mState] = GL_TRUE; } - else if (enabled == FALSE && sStateMap[mState] != GL_FALSE) + else if (enabled == DISABLED_STATE && sStateMap[mState] != GL_FALSE) { gGL.flush(); glDisable(mState); @@ -2732,7 +2732,7 @@ LLGLDepthTest::LLGLDepthTest(GLboolean depth_enabled, GLboolean write_enabled, G { // always disable depth writes if depth testing is disabled // GL spec defines this as a requirement, but some implementations allow depth writes with testing disabled // The proper way to write to depth buffer with testing disabled is to enable testing and use a depth_func of GL_ALWAYS - write_enabled = FALSE; + write_enabled = false; } if (depth_enabled != sDepthEnabled) @@ -2785,7 +2785,7 @@ void LLGLDepthTest::checkState() if (gDebugGL) { GLint func = 0; - GLboolean mask = FALSE; + GLboolean mask = false; glGetIntegerv(GL_DEPTH_FUNC, &func); glGetBooleanv(GL_DEPTH_WRITEMASK, &mask); diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 2ded1d1906..19d585e789 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -46,8 +46,8 @@ #include "glh/glh_linear.h" extern bool gDebugGL; -extern BOOL gDebugSession; -extern BOOL gDebugGLSession; +extern bool gDebugSession; +extern bool gDebugGLSession; extern llofstream gFailLog; #define LL_GL_ERRS LL_ERRS("RenderState") @@ -73,8 +73,8 @@ public: std::string getRawGLString(); // For sending to simulator - BOOL mInited; - BOOL mIsDisabled; + bool mInited; + bool mIsDisabled; // OpenGL limits S32 mMaxSamples; @@ -97,17 +97,17 @@ public: // Vendor-specific extensions bool mHasAMDAssociations = false; - BOOL mIsAMD; - BOOL mIsNVIDIA; - BOOL mIsIntel; + bool mIsAMD; + bool mIsNVIDIA; + bool mIsIntel; #if LL_DARWIN // Needed to distinguish problem cards on older Macs that break with Materials - BOOL mIsMobileGF; + bool mIsMobileGF; #endif // Whether this version of GL is good enough for SL to use - BOOL mHasRequirements; + bool mHasRequirements; S32 mDriverVersionMajor; S32 mDriverVersionMinor; @@ -241,16 +241,16 @@ protected: static boost::unordered_map sStateMap; public: - enum { CURRENT_STATE = -2 }; + 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(TRUE); } - void disable() { setEnabled(FALSE); } + void enable() { setEnabled(ENABLED_STATE); } + void disable() { setEnabled(DISABLED_STATE); } protected: LLGLenum mState; - BOOL mWasEnabled; - BOOL mIsEnabled; + bool mWasEnabled; + bool mIsEnabled; }; // New LLGLState class wrappers that don't depend on actual GL flags. @@ -284,14 +284,14 @@ public: class LLGLEnable : public LLGLState { public: - LLGLEnable(LLGLenum state) : LLGLState(state, TRUE) {} + LLGLEnable(LLGLenum state) : LLGLState(state, ENABLED_STATE) {} }; /// TODO: Being deprecated. class LLGLDisable : public LLGLState { public: - LLGLDisable(LLGLenum state) : LLGLState(state, FALSE) {} + LLGLDisable(LLGLenum state) : LLGLState(state, DISABLED_STATE) {} }; /* @@ -351,9 +351,9 @@ public: static std::list sGLQ; - BOOL mInQ; + bool mInQ; LLGLUpdate() - : mInQ(FALSE) + : mInQ(false) { } virtual ~LLGLUpdate() @@ -405,10 +405,10 @@ 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; +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 diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 65c44ed04c..405203b3de 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -309,7 +309,7 @@ LLGLSLShader::LLGLSLShader() mShaderLevel(0), mShaderGroup(SG_DEFAULT), mFeatures(), - mUniformsDirty(FALSE), + mUniformsDirty(false), mTimerQuery(0), mSamplesQuery(0), mPrimitivesQuery(0) @@ -602,11 +602,11 @@ void LLGLSLShader::attachObjects(GLuint* objects, S32 count) } } -BOOL LLGLSLShader::mapAttributes(const std::vector* attributes) +bool LLGLSLShader::mapAttributes(const std::vector* attributes) { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; - BOOL res = TRUE; + bool res = true; if (!mUsingBinaryProgram) { //before linking, make sure reserved attributes always have consistent locations @@ -663,10 +663,10 @@ BOOL LLGLSLShader::mapAttributes(const std::vector* attrib } } - return TRUE; + return true; } - return FALSE; + return false; } void LLGLSLShader::mapUniform(GLint index, const vector* uniforms) @@ -830,11 +830,11 @@ GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type, GLint return -1; } -BOOL LLGLSLShader::mapUniforms(const vector* uniforms) +bool LLGLSLShader::mapUniforms(const vector* uniforms) { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; - BOOL res = TRUE; + bool res = true; mTotalUniformSize = 0; mActiveTextureChannels = 0; @@ -1001,11 +1001,11 @@ BOOL LLGLSLShader::mapUniforms(const vector* uniforms) } -BOOL LLGLSLShader::link(BOOL suppress_errors) +bool LLGLSLShader::link(bool suppress_errors) { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; - BOOL success = LLShaderMgr::instance()->linkProgramObject(mProgramObject, suppress_errors); + bool success = LLShaderMgr::instance()->linkProgramObject(mProgramObject, suppress_errors); if (!success && !suppress_errors) { @@ -1045,7 +1045,7 @@ void LLGLSLShader::bind() if (mUniformsDirty) { LLShaderMgr::instance()->updateShaderUniforms(this); - mUniformsDirty = FALSE; + mUniformsDirty = false; } } diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index af9472e2ea..b67e06a5f2 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -182,8 +182,8 @@ public: bool attachVertexObject(std::string object); void attachObject(GLuint object); void attachObjects(GLuint* objects = NULL, S32 count = 0); - BOOL mapAttributes(const std::vector* attributes); - BOOL mapUniforms(const std::vector*); + bool mapAttributes(const std::vector* attributes); + bool mapUniforms(const std::vector*); void mapUniform(GLint index, const std::vector*); void uniform1i(U32 index, GLint i); void uniform1f(U32 index, GLfloat v); @@ -251,7 +251,7 @@ public: 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); + bool link(bool suppress_errors = false); void bind(); //helper to conditionally bind mRiggedVariant instead of this void bind(bool rigged); @@ -289,7 +289,7 @@ public: S32 mActiveTextureChannels; S32 mShaderLevel; S32 mShaderGroup; // see LLGLSLShader::eGroup - BOOL mUniformsDirty; + bool mUniformsDirty; LLShaderFeatures mFeatures; std::vector< std::pair< std::string, GLenum > > mShaderFiles; std::string mName; diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp index b616002b49..d366af0435 100644 --- a/indra/llrender/llgltexture.cpp +++ b/indra/llrender/llgltexture.cpp @@ -27,13 +27,13 @@ #include "llgltexture.h" -LLGLTexture::LLGLTexture(BOOL usemipmaps) +LLGLTexture::LLGLTexture(bool usemipmaps) { init(); mUseMipMaps = usemipmaps; } -LLGLTexture::LLGLTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps) +LLGLTexture::LLGLTexture(const U32 width, const U32 height, const U8 components, bool usemipmaps) { init(); mFullWidth = width ; @@ -43,7 +43,7 @@ LLGLTexture::LLGLTexture(const U32 width, const U32 height, const U8 components, setTexelsPerImage(); } -LLGLTexture::LLGLTexture(const LLImageRaw* raw, BOOL usemipmaps) +LLGLTexture::LLGLTexture(const LLImageRaw* raw, bool usemipmaps) { init(); mUseMipMaps = usemipmaps ; @@ -63,12 +63,12 @@ void LLGLTexture::init() mFullWidth = 0; mFullHeight = 0; mTexelsPerImage = 0 ; - mUseMipMaps = FALSE ; + mUseMipMaps = false ; mComponents = 0 ; mTextureState = NO_DELETE ; - mDontDiscard = FALSE; - mNeedsGLTexture = FALSE ; + mDontDiscard = false; + mNeedsGLTexture = false ; } void LLGLTexture::cleanup() @@ -136,7 +136,7 @@ LLImageGL* LLGLTexture::getGLTexture() const return mGLTexturep ; } -BOOL LLGLTexture::createGLTexture() +bool LLGLTexture::createGLTexture() { if(mGLTexturep.isNull()) { @@ -146,11 +146,11 @@ BOOL LLGLTexture::createGLTexture() 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) +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) ; + bool ret = mGLTexturep->createGLTexture(discard_level, imageraw, usename, to_create, category, defer_copy, tex_name) ; if(ret) { @@ -163,7 +163,7 @@ BOOL LLGLTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, return ret ; } -void LLGLTexture::setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, BOOL swap_bytes) +void LLGLTexture::setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, bool swap_bytes) { llassert(mGLTexturep.notNull()) ; @@ -218,22 +218,22 @@ LLGLuint LLGLTexture::getTexName() const return mGLTexturep->getTexName() ; } -BOOL LLGLTexture::hasGLTexture() const +bool LLGLTexture::hasGLTexture() const { if(mGLTexturep.notNull()) { return mGLTexturep->getHasGLTexture() ; } - return FALSE ; + return false ; } -BOOL LLGLTexture::getBoundRecently() const +bool LLGLTexture::getBoundRecently() const { if(mGLTexturep.notNull()) { return mGLTexturep->getBoundRecently() ; } - return FALSE ; + return false ; } LLTexUnit::eTextureType LLGLTexture::getTarget(void) const @@ -242,7 +242,7 @@ LLTexUnit::eTextureType LLGLTexture::getTarget(void) const return mGLTexturep->getTarget() ; } -BOOL LLGLTexture::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, LLGLuint use_name) +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()) ; @@ -250,7 +250,7 @@ BOOL LLGLTexture::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, 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) +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()) ; @@ -305,14 +305,14 @@ LLGLenum LLGLTexture::getPrimaryFormat() const return mGLTexturep->getPrimaryFormat() ; } -BOOL LLGLTexture::getIsAlphaMask() const +bool LLGLTexture::getIsAlphaMask() const { llassert(mGLTexturep.notNull()) ; return mGLTexturep->getIsAlphaMask() ; } -BOOL LLGLTexture::getMask(const LLVector2 &tc) +bool LLGLTexture::getMask(const LLVector2 &tc) { llassert(mGLTexturep.notNull()) ; @@ -325,14 +325,14 @@ F32 LLGLTexture::getTimePassedSinceLastBound() return mGLTexturep->getTimePassedSinceLastBound() ; } -BOOL LLGLTexture::getMissed() const +bool LLGLTexture::getMissed() const { llassert(mGLTexturep.notNull()) ; return mGLTexturep->getMissed() ; } -BOOL LLGLTexture::isJustBound() const +bool LLGLTexture::isJustBound() const { llassert(mGLTexturep.notNull()) ; @@ -360,7 +360,7 @@ U32 LLGLTexture::getTexelsInGLTexture() const return mGLTexturep->getTexelsInGLTexture() ; } -BOOL LLGLTexture::isGLTextureCreated() const +bool LLGLTexture::isGLTextureCreated() const { llassert(mGLTexturep.notNull()) ; diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h index 24849d1d1b..6012b198f3 100644 --- a/indra/llrender/llgltexture.h +++ b/indra/llrender/llgltexture.h @@ -93,9 +93,9 @@ protected: 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) ; + 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() @@ -116,25 +116,25 @@ public: /*virtual*/S32 getWidth(S32 discard_level = -1) const; /*virtual*/S32 getHeight(S32 discard_level = -1) const; - BOOL hasGLTexture() const ; + bool hasGLTexture() const ; LLGLuint getTexName() const ; - BOOL createGLTexture() ; + 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 + // 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); + 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 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); + 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 @@ -144,20 +144,20 @@ public: S32 getMaxDiscardLevel() const; S32 getDiscardLevel() const; S8 getComponents() const; - BOOL getBoundRecently() const; + bool getBoundRecently() const; S32Bytes getTextureMemory() const ; LLGLenum getPrimaryFormat() const; - BOOL getIsAlphaMask() const ; + bool getIsAlphaMask() const ; LLTexUnit::eTextureType getTarget(void) const ; - BOOL getMask(const LLVector2 &tc); + bool getMask(const LLVector2 &tc); F32 getTimePassedSinceLastBound(); - BOOL getMissed() const ; - BOOL isJustBound()const ; + bool getMissed() const ; + bool isJustBound()const ; void forceUpdateBindStats(void) const; U32 getTexelsInAtlas() const ; U32 getTexelsInGLTexture() const ; - BOOL isGLTextureCreated() const ; + bool isGLTextureCreated() const ; S32 getDiscardLevelInAtlas() const ; LLGLTextureState getTextureState() const { return mTextureState; } @@ -170,7 +170,7 @@ public: void forceActive() ; void setNoDelete() ; void dontDiscard() { mDontDiscard = 1; mTextureState = NO_DELETE; } - BOOL getDontDiscard() const { return mDontDiscard; } + bool getDontDiscard() const { return mDontDiscard; } //----------------- private: @@ -187,7 +187,7 @@ protected: S32 mBoostLevel; // enum describing priority level U32 mFullWidth; U32 mFullHeight; - BOOL mUseMipMaps; + bool mUseMipMaps; S8 mComponents; U32 mTexelsPerImage; // Texels per image. mutable S8 mNeedsGLTexture; diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 78c4c90663..7e09713054 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -126,9 +126,9 @@ U32 LLImageGL::sUniqueCount = 0; U32 LLImageGL::sBindCount = 0; S32 LLImageGL::sCount = 0; -BOOL LLImageGL::sGlobalUseAnisotropic = FALSE; +bool LLImageGL::sGlobalUseAnisotropic = false; F32 LLImageGL::sLastFrameTime = 0.f; -BOOL LLImageGL::sAllowReadBackRaw = FALSE ; +bool LLImageGL::sAllowReadBackRaw = false ; LLImageGL* LLImageGL::sDefaultGLTexture = NULL ; bool LLImageGL::sCompressTextures = false; std::set LLImageGL::sImageList; @@ -147,7 +147,7 @@ S32 LLImageGL::sCurTexPickSize = -1 ; S32 LLImageGL::sMaxCategories = 1 ; //optimization for when we don't need to calculate mIsMask -BOOL LLImageGL::sSkipAnalyzeAlpha; +bool LLImageGL::sSkipAnalyzeAlpha; //------------------------ //**************************************************************************************************** @@ -185,12 +185,12 @@ void LLImageGL::checkTexSize(bool forced) const GLint texname; glGetIntegerv(GL_TEXTURE_BINDING_2D, &texname); - BOOL error = FALSE; + bool error = false; if (texname != mTexName) { LL_INFOS() << "Bound: " << texname << " Should bind: " << mTexName << " Default: " << LLImageGL::sDefaultGLTexture->getTexName() << LL_ENDL; - error = TRUE; + error = true; if (gDebugSession) { gFailLog << "Invalid texture bound!" << std::endl; @@ -213,7 +213,7 @@ void LLImageGL::checkTexSize(bool forced) const } if(x != (mWidth >> mCurrentDiscardLevel) || y != (mHeight >> mCurrentDiscardLevel)) { - error = TRUE; + error = true; if (gDebugSession) { gFailLog << "wrong texture size and discard level!" << @@ -236,7 +236,7 @@ void LLImageGL::checkTexSize(bool forced) const //************************************************************************************** //---------------------------------------------------------------------------- -BOOL is_little_endian() +bool is_little_endian() { S32 a = 0x12345678; U8 *c = (U8*)(&a); @@ -245,7 +245,7 @@ BOOL is_little_endian() } //static -void LLImageGL::initClass(LLWindow* window, S32 num_catagories, BOOL skip_analyze_alpha /* = false */, bool thread_texture_loads /* = false */, bool thread_media_updates /* = false */) +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; @@ -356,7 +356,7 @@ void LLImageGL::updateStats(F32 current_time) //---------------------------------------------------------------------------- //static -void LLImageGL::destroyGL(BOOL save_state) +void LLImageGL::destroyGL(bool save_state) { for (S32 stage = 0; stage < gGLManager.mNumTextureImageUnits; stage++) { @@ -401,7 +401,7 @@ void LLImageGL::restoreGL() { if (glimage->getComponents() && glimage->mSaveData->getComponents()) { - glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData, 0, TRUE, glimage->getCategory()); + glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData, 0, true, glimage->getCategory()); stop_glerror(); } glimage->mSaveData = NULL; // deletes data @@ -425,30 +425,30 @@ void LLImageGL::dirtyTexOptions() //for server side use only. //static -BOOL LLImageGL::create(LLPointer& dest, BOOL usemipmaps) +bool LLImageGL::create(LLPointer& dest, bool usemipmaps) { dest = new LLImageGL(usemipmaps); - return TRUE; + return true; } //for server side use only. -BOOL LLImageGL::create(LLPointer& dest, U32 width, U32 height, U8 components, BOOL usemipmaps) +bool LLImageGL::create(LLPointer& dest, U32 width, U32 height, U8 components, bool usemipmaps) { dest = new LLImageGL(width, height, components, usemipmaps); - return TRUE; + return true; } //for server side use only. -BOOL LLImageGL::create(LLPointer& dest, const LLImageRaw* imageraw, BOOL usemipmaps) +bool LLImageGL::create(LLPointer& dest, const LLImageRaw* imageraw, bool usemipmaps) { dest = new LLImageGL(imageraw, usemipmaps); - return TRUE; + return true; } //---------------------------------------------------------------------------- -LLImageGL::LLImageGL(BOOL usemipmaps) -: mSaveData(0), mExternalTexture(FALSE) +LLImageGL::LLImageGL(bool usemipmaps) +: mSaveData(0), mExternalTexture(false) { init(usemipmaps); setSize(0, 0, 0); @@ -456,8 +456,8 @@ LLImageGL::LLImageGL(BOOL usemipmaps) sCount++; } -LLImageGL::LLImageGL(U32 width, U32 height, U8 components, BOOL usemipmaps) -: mSaveData(0), mExternalTexture(FALSE) +LLImageGL::LLImageGL(U32 width, U32 height, U8 components, bool usemipmaps) +: mSaveData(0), mExternalTexture(false) { llassert( components <= 4 ); init(usemipmaps); @@ -466,8 +466,8 @@ LLImageGL::LLImageGL(U32 width, U32 height, U8 components, BOOL usemipmaps) sCount++; } -LLImageGL::LLImageGL(const LLImageRaw* imageraw, BOOL usemipmaps) -: mSaveData(0), mExternalTexture(FALSE) +LLImageGL::LLImageGL(const LLImageRaw* imageraw, bool usemipmaps) +: mSaveData(0), mExternalTexture(false) { init(usemipmaps); setSize(0, 0, 0); @@ -508,7 +508,7 @@ LLImageGL::~LLImageGL() } } -void LLImageGL::init(BOOL usemipmaps) +void LLImageGL::init(bool usemipmaps) { #if LL_IMAGEGL_THREAD_CHECK mActiveThread = LLThread::currentID(); @@ -525,14 +525,14 @@ void LLImageGL::init(BOOL usemipmaps) mPickMaskWidth = 0; mPickMaskHeight = 0; mUseMipMaps = usemipmaps; - mHasExplicitFormat = FALSE; + mHasExplicitFormat = false; - mIsMask = FALSE; - mNeedsAlphaAndPickMask = TRUE ; + mIsMask = false; + mNeedsAlphaAndPickMask = true ; mAlphaStride = 0 ; mAlphaOffset = 0 ; - mGLTextureCreated = FALSE ; + mGLTextureCreated = false ; mTexName = 0; mWidth = 0; mHeight = 0; @@ -561,10 +561,10 @@ void LLImageGL::init(BOOL usemipmaps) mFormatInternal = -1; mFormatPrimary = (LLGLenum) 0; mFormatType = GL_UNSIGNED_BYTE; - mFormatSwapBytes = FALSE; + mFormatSwapBytes = false; #ifdef DEBUG_MISS - mMissed = FALSE; + mMissed = false; #endif mCategory = -1; @@ -682,12 +682,12 @@ void LLImageGL::forceUpdateBindStats(void) const mLastBindTime = sLastFrameTime; } -BOOL LLImageGL::updateBindStats() const +bool LLImageGL::updateBindStats() const { if (mTexName != 0) { #ifdef DEBUG_MISS - mMissed = ! getIsResident(TRUE); + mMissed = ! getIsResident(true); #endif sBindCount++; if (mLastBindTime != sLastFrameTime) @@ -696,10 +696,10 @@ BOOL LLImageGL::updateBindStats() const sUniqueCount++; mLastBindTime = sLastFrameTime; - return TRUE ; + return true ; } } - return FALSE ; + return false ; } F32 LLImageGL::getTimePassedSinceLastBound() @@ -707,11 +707,11 @@ F32 LLImageGL::getTimePassedSinceLastBound() return sLastFrameTime - mLastBindTime ; } -void LLImageGL::setExplicitFormat( LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, BOOL swap_bytes ) +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; + mHasExplicitFormat = true; mFormatInternal = internal_format; mFormatPrimary = primary_format; if(type_format == 0) @@ -732,10 +732,10 @@ void LLImageGL::setImage(const LLImageRaw* imageraw) (imageraw->getHeight() == getHeight(mCurrentDiscardLevel)) && (imageraw->getComponents() == getComponents())); const U8* rawdata = imageraw->getData(); - setImage(rawdata, FALSE); + setImage(rawdata, false); } -BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips /* = FALSE */, S32 usename /* = 0 */) +bool LLImageGL::setImage(const U8* data_in, bool data_hasmips /* = false */, S32 usename /* = 0 */) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; @@ -917,7 +917,7 @@ BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips /* = FALSE */, S32 } mGLTextureCreated = false; - return FALSE; + return false; } else { @@ -1018,10 +1018,10 @@ BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips /* = FALSE */, S32 } stop_glerror(); mGLTextureCreated = true; - return TRUE; + return true; } -BOOL LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image) +bool LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image) { //not compatible with core GL profile llassert(!LLRender::sGLCoreProfile); @@ -1029,7 +1029,7 @@ BOOL LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image) if (gGLManager.mIsDisabled) { LL_WARNS() << "Trying to create a texture while GL is disabled!" << LL_ENDL; - return FALSE; + return false; } llassert(gGLManager.mInited); stop_glerror(); @@ -1048,7 +1048,7 @@ BOOL LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image) if (!setSize(w, h, raw_image->getComponents(), discard_level)) { LL_WARNS() << "Trying to create a texture with incorrect dimensions!" << LL_ENDL; - return FALSE; + return false; } if (!mHasExplicitFormat) @@ -1097,7 +1097,7 @@ BOOL LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image) stop_glerror(); } - return TRUE ; + return true ; } void LLImageGL::postAddToAtlas() @@ -1167,31 +1167,31 @@ void sub_image_lines(U32 target, S32 miplevel, S32 x_offset, S32 y_offset, S32 w } } -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) +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; + 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; + 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; + 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); + setImage(datap, false, tex_name); } else { @@ -1243,7 +1243,7 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 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); + bool res = gGL.getTexUnit(0)->bindManual(mBindTarget, tex_name); if (!res) LL_ERRS() << "LLImageGL::setSubImage(): bindTexture failed" << LL_ENDL; stop_glerror(); @@ -1273,28 +1273,28 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 stop_glerror(); mGLTextureCreated = true; } - return 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) +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) +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; + return true; } else { - return FALSE; + return false; } } @@ -1508,7 +1508,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt //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() +bool LLImageGL::createGLTexture() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; checkActiveThread(); @@ -1516,7 +1516,7 @@ BOOL LLImageGL::createGLTexture() if (gGLManager.mIsDisabled) { LL_WARNS() << "Trying to create a texture while GL is disabled!" << LL_ENDL; - return FALSE; + return false; } mGLTextureCreated = false ; //do not save this texture when gl is destroyed. @@ -1536,13 +1536,13 @@ BOOL LLImageGL::createGLTexture() if (!mTexName) { LL_WARNS() << "LLImageGL::createGLTexture failed to make an empty texture" << LL_ENDL; - return FALSE; + return false; } - return TRUE ; + 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) +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(); @@ -1550,7 +1550,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S if (gGLManager.mIsDisabled) { LL_WARNS() << "Trying to create a texture while GL is disabled!" << LL_ENDL; - return FALSE; + return false; } llassert(gGLManager.mInited); @@ -1560,7 +1560,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S { LL_WARNS() << "Trying to create a texture from invalid image data" << LL_ENDL; mGLTextureCreated = false; - return FALSE; + return false; } if (discard_level < 0) @@ -1581,7 +1581,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S { LL_WARNS() << "Trying to create a texture with incorrect dimensions!" << LL_ENDL; mGLTextureCreated = false; - return FALSE; + return false; } if (mHasExplicitFormat && @@ -1590,7 +1590,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S { LL_WARNS() << "Incorrect format: " << std::hex << mFormatPrimary << " components: " << (U32)mComponents << LL_ENDL; - mHasExplicitFormat = FALSE; + mHasExplicitFormat = false; } if( !mHasExplicitFormat ) @@ -1632,15 +1632,15 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S mCurrentDiscardLevel = discard_level; mLastBindTime = sLastFrameTime; mGLTextureCreated = false; - return TRUE ; + return true ; } setCategory(category); const U8* rawdata = imageraw->getData(); - return createGLTexture(discard_level, rawdata, FALSE, usename, defer_copy, tex_name); + 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) +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; @@ -1713,7 +1713,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ LL_PROFILE_ZONE_NAMED("cglt - late setImage"); if (!setImage(data_in, data_hasmips, new_texname)) { - return FALSE; + return false; } } @@ -1751,7 +1751,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ mLastBindTime = sLastFrameTime; checkActiveThread(); - return TRUE; + return true; } void LLImageGL::syncToMainThread(LLGLuint new_tex_name) @@ -1821,7 +1821,7 @@ void LLImageGL::syncTexName(LLGLuint texname) } } -BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const +bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const { llassert_always(sAllowReadBackRaw) ; //LL_ERRS() << "should not call this function!" << LL_ENDL ; @@ -1833,7 +1833,7 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre if (mTexName == 0 || discard_level < mCurrentDiscardLevel || discard_level > mMaxDiscardLevel ) { - return FALSE; + return false; } S32 gl_discard = discard_level - mCurrentDiscardLevel; @@ -1850,7 +1850,7 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre if (glwidth == 0) { // No mip data smaller than current discard level - return FALSE; + return false; } S32 width = getWidth(discard_level); @@ -1858,14 +1858,14 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre S32 ncomponents = getComponents(); if (ncomponents == 0) { - return FALSE; + 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 ; + return false ; } if (width <= 0 || width > 2048 || height <= 0 || height > 2048 || ncomponents < 1 || ncomponents > 4) @@ -1897,7 +1897,7 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre { 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 ; + return false ; } glGetCompressedTexImage(mTarget, gl_discard, (GLvoid*)(imageraw->getData())); @@ -1909,7 +1909,7 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre { LL_WARNS() << "Memory allocation failed for reading back texture." << LL_ENDL ; LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ; - return FALSE ; + return false ; } glGetTexImage(GL_TEXTURE_2D, gl_discard, mFormatPrimary, mFormatType, (GLvoid*)(imageraw->getData())); @@ -1927,11 +1927,11 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre LL_WARNS() << "GL Error happens after reading back texture. Error code: " << error << LL_ENDL ; } - return FALSE ; + return false ; } //----------------------------------------------------------------------------------------------- - return TRUE ; + return true ; } void LLImageGL::destroyGLTexture() @@ -1948,7 +1948,7 @@ void LLImageGL::destroyGLTexture() LLImageGL::deleteTextures(1, &mTexName); mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel. mTexName = 0; - mGLTextureCreated = FALSE ; + mGLTextureCreated = false ; } } @@ -1999,7 +1999,7 @@ void LLImageGL::setFilteringOption(LLTexUnit::eTextureFilterOptions option) } } -BOOL LLImageGL::getIsResident(BOOL test_now) +bool LLImageGL::getIsResident(bool test_now) { if (test_now) { @@ -2009,7 +2009,7 @@ BOOL LLImageGL::getIsResident(BOOL test_now) } else { - mIsResident = FALSE; + mIsResident = false; } } @@ -2077,12 +2077,12 @@ bool LLImageGL::isJustBound() const return sLastFrameTime - mLastBindTime < 0.5f; } -BOOL LLImageGL::getBoundRecently() const +bool LLImageGL::getBoundRecently() const { - return (BOOL)(sLastFrameTime - mLastBindTime < MIN_TEXTURE_LIFETIME); + return (bool)(sLastFrameTime - mLastBindTime < MIN_TEXTURE_LIFETIME); } -BOOL LLImageGL::getIsAlphaMask() const +bool LLImageGL::getIsAlphaMask() const { llassert_always(!sSkipAnalyzeAlpha); return mIsMask; @@ -2095,7 +2095,7 @@ void LLImageGL::setTarget(const LLGLenum target, const LLTexUnit::eTextureType b } const S8 INVALID_OFFSET = -99 ; -void LLImageGL::setNeedsAlphaAndPickMask(BOOL need_mask) +void LLImageGL::setNeedsAlphaAndPickMask(bool need_mask) { if(mNeedsAlphaAndPickMask != need_mask) { @@ -2108,7 +2108,7 @@ void LLImageGL::setNeedsAlphaAndPickMask(BOOL need_mask) else //do not need alpha mask { mAlphaOffset = INVALID_OFFSET ; - mIsMask = FALSE; + mIsMask = false; } } } @@ -2133,8 +2133,8 @@ void LLImageGL::calcAlphaChannelOffsetAndStride() case GL_RED: case GL_RGB: case GL_SRGB: - mNeedsAlphaAndPickMask = FALSE; - mIsMask = FALSE; + mNeedsAlphaAndPickMask = false; + mIsMask = false; return; //no alpha channel. case GL_RGBA: case GL_SRGB_ALPHA: @@ -2181,8 +2181,8 @@ void LLImageGL::calcAlphaChannelOffsetAndStride() { LL_WARNS() << "Cannot analyze alpha for image with format type " << std::hex << mFormatType << std::dec << LL_ENDL; - mNeedsAlphaAndPickMask = FALSE ; - mIsMask = FALSE; + mNeedsAlphaAndPickMask = false ; + mIsMask = false; } } @@ -2279,11 +2279,11 @@ void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h) (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 + mIsMask = false; // not suitable for masking } else { - mIsMask = TRUE; + mIsMask = true; } } @@ -2383,9 +2383,9 @@ void LLImageGL::updatePickMask(S32 width, S32 height, const U8* data_in) } } -BOOL LLImageGL::getMask(const LLVector2 &tc) +bool LLImageGL::getMask(const LLVector2 &tc) { - BOOL res = TRUE; + bool res = true; if (mPickMask) { @@ -2429,13 +2429,13 @@ BOOL LLImageGL::getMask(const LLVector2 &tc) S32 idx = y*mPickMaskWidth+x; S32 offset = idx%8; - res = mPickMask[idx/8] & (1 << offset) ? TRUE : FALSE; + res = mPickMask[idx/8] & (1 << offset) ? true : false; } return res; } -void LLImageGL::setCurTexSizebar(S32 index, BOOL set_pick_size) +void LLImageGL::setCurTexSizebar(S32 index, bool set_pick_size) { sCurTexSizeBar = index ; diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 1b77fd0964..60fa1258b6 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -68,7 +68,7 @@ public: static S64 dataFormatBytes(S32 dataformat, S32 width, S32 height); static S32 dataFormatComponents(S32 dataformat); - BOOL updateBindStats() const ; + bool updateBindStats() const ; F32 getTimePassedSinceLastBound(); void forceUpdateBindStats(void) const; @@ -76,7 +76,7 @@ public: static void updateStats(F32 current_time); // Save off / restore GL textures - static void destroyGL(BOOL save_state = TRUE); + static void destroyGL(bool save_state = true); static void restoreGL(); static void dirtyTexOptions(); @@ -85,14 +85,14 @@ public: //for server side use only. // Not currently necessary for LLImageGL, but required in some derived classes, // so include for compatability - static BOOL create(LLPointer& dest, BOOL usemipmaps = TRUE); - static BOOL create(LLPointer& dest, U32 width, U32 height, U8 components, BOOL usemipmaps = TRUE); - static BOOL create(LLPointer& dest, const LLImageRaw* imageraw, BOOL usemipmaps = TRUE); + static bool create(LLPointer& dest, bool usemipmaps = true); + static bool create(LLPointer& dest, U32 width, U32 height, U8 components, bool usemipmaps = true); + static bool create(LLPointer& 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); + 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); @@ -112,29 +112,29 @@ public: 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, + 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); + 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); + 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); + 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; + 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 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; } @@ -147,18 +147,18 @@ public: U8 getComponents() const { return mComponents; } S64 getBytes(S32 discard_level = -1) const; S64 getMipBytes(S32 discard_level = -1) const; - BOOL getBoundRecently() const; + bool getBoundRecently() const; bool isJustBound() const; - BOOL getHasExplicitFormat() const { return mHasExplicitFormat; } + bool getHasExplicitFormat() const { return mHasExplicitFormat; } LLGLenum getPrimaryFormat() const { return mFormatPrimary; } LLGLenum getFormatType() const { return mFormatType; } - BOOL getHasGLTexture() const { return mTexName != 0; } + bool getHasGLTexture() const { return mTexName != 0; } LLGLuint getTexName() const { return mTexName; } - BOOL getIsAlphaMask() const; + bool getIsAlphaMask() const; - BOOL getIsResident(BOOL test_now = FALSE); // not const + bool getIsResident(bool test_now = false); // not const void setTarget(const LLGLenum target, const LLTexUnit::eTextureType bind_target); @@ -166,11 +166,11 @@ public: 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; } + 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); + bool getMask(const LLVector2 &tc); void checkTexSize(bool forced = false) const ; @@ -192,12 +192,12 @@ public: U32 getTexelsInGLTexture()const {return mTexelsInGLTexture;} - void init(BOOL usemipmaps); + 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); + void setNeedsAlphaAndPickMask(bool need_mask); - BOOL preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image); + bool preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image); void postAddToAtlas() ; #if LL_IMAGEGL_THREAD_CHECK @@ -222,11 +222,11 @@ private: U16 mPickMaskWidth; U16 mPickMaskHeight; S8 mUseMipMaps; - BOOL mHasExplicitFormat; // If false (default), GL format is f(mComponents) + bool mHasExplicitFormat; // If false (default), GL format is f(mComponents) bool mAutoGenMips = false; - BOOL mIsMask; - BOOL mNeedsAlphaAndPickMask; + bool mIsMask; + bool mNeedsAlphaAndPickMask; S8 mAlphaStride ; S8 mAlphaOffset ; @@ -261,9 +261,9 @@ protected: 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 mFormatSwapBytes;// if true, use glPixelStorei(GL_UNPACK_SWAP_BYTES, 1) - BOOL mExternalTexture; + bool mExternalTexture; // STATICS public: @@ -275,28 +275,28 @@ public: // 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 bool sGlobalUseAnisotropic; static LLImageGL* sDefaultGLTexture ; - static BOOL sAutomatedTest; + static bool sAutomatedTest; static bool sCompressTextures; //use GL texture compression #if DEBUG_MISS - BOOL mMissed; // Missed on last bind? - BOOL getMissed() const { return mMissed; }; + bool mMissed; // Missed on last bind? + bool getMissed() const { return mMissed; }; #else - BOOL getMissed() const { return FALSE; }; + 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 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; + 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 ; + static bool sAllowReadBackRaw ; // //**************************************************************************************************** //The below for texture auditing use only @@ -317,7 +317,7 @@ public: static S32 sCurTexSizeBar ; static S32 sCurTexPickSize ; - static void setCurTexSizebar(S32 index, BOOL set_pick_size = TRUE) ; + static void setCurTexSizebar(S32 index, bool set_pick_size = true) ; static void resetCurTexSizebar(); //**************************************************************************************************** diff --git a/indra/llrender/llpostprocess.cpp b/indra/llrender/llpostprocess.cpp index 0f8655132b..6e1375c65f 100644 --- a/indra/llrender/llpostprocess.cpp +++ b/indra/llrender/llpostprocess.cpp @@ -165,7 +165,7 @@ void LLPostProcess::invalidate() mSceneRenderTexture = NULL ; mNoiseTexture = NULL ; mTempBloomTexture = NULL ; - initialized = FALSE ; + initialized = false ; } void LLPostProcess::apply(unsigned int width, unsigned int height) @@ -367,7 +367,7 @@ void LLPostProcess::createTexture(LLPointer& texture, unsigned int wi { std::vector data(width * height * 4, 0) ; - texture = new LLImageGL(FALSE) ; + texture = new LLImageGL(false) ; if(texture->createGLTexture()) { gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, texture->getTexName()); @@ -387,7 +387,7 @@ void LLPostProcess::createNoiseTexture(LLPointer& texture) } } - texture = new LLImageGL(FALSE) ; + texture = new LLImageGL(false) ; if(texture->createGLTexture()) { gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, texture->getTexName()); diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index ee66122774..66252facc4 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -1079,7 +1079,7 @@ void LLRender::syncMatrices() if (shader->getUniformLocation(LLShaderMgr::INVERSE_PROJECTION_MATRIX)) { glh::matrix4f inv_proj = mat.inverse(); - shader->uniformMatrix4fv(LLShaderMgr::INVERSE_PROJECTION_MATRIX, 1, FALSE, inv_proj.m); + shader->uniformMatrix4fv(LLShaderMgr::INVERSE_PROJECTION_MATRIX, 1, false, inv_proj.m); } // Used by some full screen effects - such as full screen lights, glow, etc. diff --git a/indra/llrender/llrender2dutils.cpp b/indra/llrender/llrender2dutils.cpp index df1169b315..aeaf3c3db0 100644 --- a/indra/llrender/llrender2dutils.cpp +++ b/indra/llrender/llrender2dutils.cpp @@ -91,13 +91,13 @@ void gl_draw_x(const LLRect& rect, const LLColor4& color) } -void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, S32 pixel_offset, BOOL filled) +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) +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; @@ -115,7 +115,7 @@ void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixe } -void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, BOOL filled ) +void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, bool filled ) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); @@ -143,14 +143,14 @@ void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, BOOL filled ) } } -void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, BOOL filled ) +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 ) +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 ); @@ -243,7 +243,7 @@ void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2, const LLColor4 &color ) gGL.end(); } -void gl_triangle_2d(S32 x1, S32 y1, S32 x2, S32 y2, S32 x3, S32 y3, const LLColor4& color, BOOL filled) +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); @@ -322,7 +322,7 @@ void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLTexture* image, 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) +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) { @@ -338,7 +338,7 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border 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) +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(); @@ -729,7 +729,7 @@ void gl_line_3d( const LLVector3& start, const LLVector3& end, const LLColor4& c 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) +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) { @@ -772,7 +772,7 @@ void gl_arc_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, BOOL filled, F gGL.popUIMatrix(); } -void gl_circle_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, BOOL filled) +void gl_circle_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, bool filled) { gGL.pushUIMatrix(); { @@ -833,7 +833,7 @@ void gl_deep_circle( F32 radius, F32 depth, S32 steps ) gGL.end(); } -void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color, S32 steps, BOOL render_center ) +void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color, S32 steps, bool render_center ) { gGL.pushUIMatrix(); { diff --git a/indra/llrender/llrender2dutils.h b/indra/llrender/llrender2dutils.h index 298b357a38..63bb9c1317 100644 --- a/indra/llrender/llrender2dutils.h +++ b/indra/llrender/llrender2dutils.h @@ -48,25 +48,25 @@ 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_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(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_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_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); @@ -76,8 +76,8 @@ void gl_draw_scaled_target(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* 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_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); @@ -110,12 +110,12 @@ void gl_segmented_rect_2d_tex(const S32 left, const S32 top, const S32 right, co 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 ) +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) +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 ); } diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index f78be910d2..edb5f65e1f 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -65,14 +65,14 @@ LLShaderMgr * LLShaderMgr::instance() return sInstance; } -BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) +bool LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { llassert_always(shader != NULL); LLShaderFeatures *features = & shader->mFeatures; if (features->attachNothing) { - return TRUE; + return true; } ////////////////////////////////////// // Attach Vertex Shader Features First @@ -83,7 +83,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachVertexObject("windlight/atmosphericsVarsV.glsl")) { - return FALSE; + return false; } } @@ -91,7 +91,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachVertexObject("windlight/atmosphericsHelpersV.glsl")) { - return FALSE; + return false; } } @@ -101,40 +101,40 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachVertexObject("lighting/lightFuncSpecularV.glsl")) { - return FALSE; + return false; } if (!features->isAlphaLighting) { if (!shader->attachVertexObject("lighting/sumLightsSpecularV.glsl")) { - return FALSE; + return false; } } if (!shader->attachVertexObject("lighting/lightSpecularV.glsl")) { - return FALSE; + return false; } } else { if (!shader->attachVertexObject("lighting/lightFuncV.glsl")) { - return FALSE; + return false; } if (!features->isAlphaLighting) { if (!shader->attachVertexObject("lighting/sumLightsV.glsl")) { - return FALSE; + return false; } } if (!shader->attachVertexObject("lighting/lightV.glsl")) { - return FALSE; + return false; } } } @@ -144,16 +144,16 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachVertexObject("environment/srgbF.glsl")) // NOTE -- "F" suffix is superfluous here, there is nothing fragment specific in srgbF { - return FALSE; + return false; } if (!shader->attachVertexObject("windlight/atmosphericsFuncs.glsl")) { - return FALSE; + return false; } if (!shader->attachVertexObject("windlight/atmosphericsV.glsl")) { - return FALSE; + return false; } } @@ -161,7 +161,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachVertexObject("avatar/avatarSkinV.glsl")) { - return FALSE; + return false; } } @@ -170,13 +170,13 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) shader->mRiggedVariant = shader; if (!shader->attachVertexObject("avatar/objectSkinV.glsl")) { - return FALSE; + return false; } } if (!shader->attachVertexObject("deferred/textureUtilV.glsl")) { - return FALSE; + return false; } /////////////////////////////////////// @@ -188,7 +188,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("environment/srgbF.glsl")) { - return FALSE; + return false; } } @@ -196,7 +196,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("windlight/atmosphericsVarsF.glsl")) { - return FALSE; + return false; } } @@ -204,7 +204,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("windlight/atmosphericsHelpersF.glsl")) { - return FALSE; + return false; } } @@ -213,7 +213,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("deferred/deferredUtil.glsl")) { - return FALSE; + return false; } } @@ -221,7 +221,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("deferred/screenSpaceReflUtil.glsl")) { - return FALSE; + return false; } } @@ -229,7 +229,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("deferred/shadowUtil.glsl")) { - return FALSE; + return false; } } @@ -237,7 +237,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("deferred/reflectionProbeF.glsl")) { - return FALSE; + return false; } } @@ -245,7 +245,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("deferred/aoUtil.glsl")) { - return FALSE; + return false; } } @@ -253,7 +253,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("windlight/gammaF.glsl")) { - return FALSE; + return false; } } @@ -261,19 +261,19 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("environment/encodeNormF.glsl")) { - return FALSE; + return false; } } if (features->hasAtmospherics || features->isDeferred) { if (!shader->attachFragmentObject("windlight/atmosphericsFuncs.glsl")) { - return FALSE; + return false; } if (!shader->attachFragmentObject("windlight/atmosphericsF.glsl")) { - return FALSE; + return false; } } @@ -282,7 +282,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("environment/waterFogF.glsl")) { - return FALSE; + return false; } } @@ -294,14 +294,14 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("lighting/lightAlphaMaskNonIndexedF.glsl")) { - return FALSE; + return false; } } else { if (!shader->attachFragmentObject("lighting/lightNonIndexedF.glsl")) { - return FALSE; + return false; } } } @@ -311,14 +311,14 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("lighting/lightAlphaMaskF.glsl")) { - return FALSE; + return false; } } else { if (!shader->attachFragmentObject("lighting/lightF.glsl")) { - return FALSE; + return false; } } shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels-1, 1); @@ -329,18 +329,18 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachVertexObject("objects/nonindexedTextureV.glsl")) { - return FALSE; + return false; } } else { if (!shader->attachVertexObject("objects/indexedTextureV.glsl")) { - return FALSE; + return false; } } - return TRUE; + return true; } //============================================================================ @@ -414,7 +414,7 @@ void LLShaderMgr::dumpShaderSource(U32 shader_code_count, GLchar** shader_code_t LL_CONT << LL_ENDL; } -void LLShaderMgr::dumpObjectLog(GLuint ret, BOOL warns, const std::string& filename) +void LLShaderMgr::dumpObjectLog(GLuint ret, bool warns, const std::string& filename) { std::string log; log = get_object_log(ret); @@ -853,7 +853,7 @@ GLuint LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_lev { //an error occured, print log LL_WARNS("ShaderLoading") << "GLSL Compilation Error:" << LL_ENDL; - dumpObjectLog(ret, TRUE, open_file_name); + dumpObjectLog(ret, true, open_file_name); dumpShaderSource(shader_code_count, shader_code_text); glDeleteShader(ret); //no longer need handle ret = 0; @@ -895,7 +895,7 @@ GLuint LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_lev return ret; } -BOOL LLShaderMgr::linkProgramObject(GLuint obj, BOOL suppress_errors) +bool LLShaderMgr::linkProgramObject(GLuint obj, bool suppress_errors) { //check for errors { @@ -912,7 +912,7 @@ BOOL LLShaderMgr::linkProgramObject(GLuint obj, BOOL suppress_errors) { //an error occured, print log LL_SHADER_LOADING_WARNS() << "GLSL Linker Error:" << LL_ENDL; - dumpObjectLog(obj, TRUE, "linker"); + dumpObjectLog(obj, true, "linker"); return success; } } @@ -923,12 +923,12 @@ BOOL LLShaderMgr::linkProgramObject(GLuint obj, BOOL suppress_errors) { LL_SHADER_LOADING_WARNS() << "GLSL Linker: Running in Software:" << LL_ENDL; success = GL_FALSE; - suppress_errors = FALSE; + suppress_errors = false; } return success; } -BOOL LLShaderMgr::validateProgramObject(GLuint obj) +bool LLShaderMgr::validateProgramObject(GLuint obj) { //check program validity against current GL glValidateProgram(obj); @@ -941,7 +941,7 @@ BOOL LLShaderMgr::validateProgramObject(GLuint obj) } else { - dumpObjectLog(obj, FALSE); + dumpObjectLog(obj, false); } return success; diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 79a24773e1..781fcd95e0 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -296,11 +296,11 @@ public: virtual void initAttribsAndUniforms(void); - BOOL attachShaderFeatures(LLGLSLShader * shader); - void dumpObjectLog(GLuint ret, BOOL warns = TRUE, const std::string& filename = ""); + 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); + 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* defines = NULL, S32 texture_index_channels = -1); // Implemented in the application to actually point to the shader directory. diff --git a/indra/llrender/lltexturemanagerbridge.h b/indra/llrender/lltexturemanagerbridge.h index f61433ea4d..bbbbbf9f9e 100644 --- a/indra/llrender/lltexturemanagerbridge.h +++ b/indra/llrender/lltexturemanagerbridge.h @@ -36,8 +36,8 @@ class LLTextureManagerBridge public: virtual ~LLTextureManagerBridge() {} - virtual LLPointer getLocalTexture(BOOL usemipmaps = TRUE, BOOL generate_gl_tex = TRUE) = 0; - virtual LLPointer getLocalTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps, BOOL generate_gl_tex = TRUE) = 0; + virtual LLPointer getLocalTexture(bool usemipmaps = true, bool generate_gl_tex = true) = 0; + virtual LLPointer 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; }; diff --git a/indra/llrender/lluiimage.inl b/indra/llrender/lluiimage.inl index f5227556f0..8fee1c68c3 100644 --- a/indra/llrender/lluiimage.inl +++ b/indra/llrender/lluiimage.inl @@ -36,7 +36,7 @@ void LLUIImage::draw(S32 x, S32 y, S32 width, S32 height, const LLColor4& color) width, height, mImage, color, - FALSE, + false, mClipRegion, mScaleRegion, mScaleStyle == SCALE_INNER); @@ -49,7 +49,7 @@ void LLUIImage::drawSolid(S32 x, S32 y, S32 width, S32 height, const LLColor4& c width, height, mImage, color, - TRUE, + true, mClipRegion, mScaleRegion, mScaleStyle == SCALE_INNER); diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index f2d146d4bb..38ca799165 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -253,7 +253,7 @@ private: : LLVertexBuffer(typemask) {} - bool allocateBuffer(S32 nverts, S32 nindices, BOOL create) { return allocateBuffer(nverts, nindices); } + bool allocateBuffer(S32 nverts, S32 nindices, bool create) { return allocateBuffer(nverts, nindices); } public: -- cgit v1.2.3 From 816983854284913da6973a0976fd7175debeb09c Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 20 Feb 2024 01:00:23 +0100 Subject: Fix incorrect datatype being assigned to GLboolean --- indra/llrender/llgl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index b6cbef142c..3d1b978c09 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -2732,7 +2732,7 @@ LLGLDepthTest::LLGLDepthTest(GLboolean depth_enabled, GLboolean write_enabled, G { // always disable depth writes if depth testing is disabled // GL spec defines this as a requirement, but some implementations allow depth writes with testing disabled // The proper way to write to depth buffer with testing disabled is to enable testing and use a depth_func of GL_ALWAYS - write_enabled = false; + write_enabled = GL_FALSE; } if (depth_enabled != sDepthEnabled) @@ -2785,7 +2785,7 @@ void LLGLDepthTest::checkState() if (gDebugGL) { GLint func = 0; - GLboolean mask = false; + GLboolean mask = GL_FALSE; glGetIntegerv(GL_DEPTH_FUNC, &func); glGetBooleanv(GL_DEPTH_WRITEMASK, &mask); -- cgit v1.2.3 From 8be121e7cd88e692dad26510426defc5fc1df358 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Tue, 20 Feb 2024 02:49:25 +0200 Subject: viewer#840 Xcode buildfix --- indra/llrender/llfontfreetype.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp index ffeed89454..3c702dc53d 100644 --- a/indra/llrender/llfontfreetype.cpp +++ b/indra/llrender/llfontfreetype.cpp @@ -423,7 +423,7 @@ bool LLFontFreetype::hasGlyph(llwchar wch) const LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch) const { if (mFTFace == NULL) - return false; + return NULL; llassert(!mIsFallback); //LL_DEBUGS() << "Adding new glyph for " << wch << " to font" << LL_ENDL; -- cgit v1.2.3 From a156998fa6abfca95740595b4cf2259a452cd0ce Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 6 May 2024 23:19:54 +0300 Subject: viewer#799 Account for reflection probes' memory --- indra/llrender/llcubemaparray.cpp | 6 ++++++ indra/llrender/llimagegl.cpp | 12 ++++++++---- indra/llrender/llimagegl.h | 8 ++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llcubemaparray.cpp b/indra/llrender/llcubemaparray.cpp index ed0ad07dc0..4f8d4015b6 100644 --- a/indra/llrender/llcubemaparray.cpp +++ b/indra/llrender/llcubemaparray.cpp @@ -42,6 +42,8 @@ //#pragma optimize("", off) +using namespace LLImageGLMemory; + // MUST match order of OpenGL face-layers GLenum LLCubeMapArray::sTargets[6] = { @@ -128,6 +130,8 @@ void LLCubeMapArray::allocate(U32 resolution, U32 components, U32 count, bool us U32 mip = 0; + free_cur_tex_image(); + while (resolution >= 1) { glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, format, resolution, resolution, count * 6, 0, @@ -141,6 +145,8 @@ void LLCubeMapArray::allocate(U32 resolution, U32 components, U32 count, bool us ++mip; } + alloc_tex_image(resolution * 6, resolution, format); + mImage->setAddressMode(LLTexUnit::TAM_CLAMP); if (use_mips) diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index ed729f64e3..bdc9ec5c94 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -63,7 +63,7 @@ static U64 sTextureBytes = 0; // track a texture alloc on the currently bound texture. // asserts that no currently tracked alloc exists -static void alloc_tex_image(U32 width, U32 height, U32 pixformat) +void LLImageGLMemory::alloc_tex_image(U32 width, U32 height, U32 pixformat) { U32 texUnit = gGL.getCurrentTexUnitIndex(); U32 texName = gGL.getTexUnit(texUnit)->getCurrTexture(); @@ -81,7 +81,7 @@ static void alloc_tex_image(U32 width, U32 height, U32 pixformat) } // track texture free on given texName -static void free_tex_image(U32 texName) +void LLImageGLMemory::free_tex_image(U32 texName) { sTexMemMutex.lock(); auto iter = sTextureAllocs.find(texName); @@ -98,7 +98,7 @@ static void free_tex_image(U32 texName) } // track texture free on given texNames -static void free_tex_images(U32 count, const U32* texNames) +void LLImageGLMemory::free_tex_images(U32 count, const U32* texNames) { for (int i = 0; i < count; ++i) { @@ -107,13 +107,15 @@ static void free_tex_images(U32 count, const U32* texNames) } // track texture free on currently bound texture -static void free_cur_tex_image() +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() { @@ -289,6 +291,8 @@ S32 LLImageGL::dataFormatBits(S32 dataformat) 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; diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 60fa1258b6..18187734f1 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -47,6 +47,14 @@ 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 { -- cgit v1.2.3 From f9473e8afcb624cc1b101195bf15943ec372b56f Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Mon, 6 May 2024 16:52:34 +0200 Subject: secondlife/viewer#1333 BOOL to bool conversion leftovers: ternaries --- indra/llrender/llimagegl.cpp | 2 +- indra/llrender/llrendertarget.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index bdc9ec5c94..39345929b3 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -2436,7 +2436,7 @@ bool LLImageGL::getMask(const LLVector2 &tc) S32 idx = y*mPickMaskWidth+x; S32 offset = idx%8; - res = mPickMask[idx/8] & (1 << offset) ? true : false; + res = (mPickMask[idx/8] & (1 << offset)) != 0; } return res; diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 88c48e5166..8c05a47c39 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -545,7 +545,7 @@ void LLRenderTarget::flush() bool LLRenderTarget::isComplete() const { - return (!mTex.empty() || mDepth) ? true : false; + return !mTex.empty() || mDepth; } void LLRenderTarget::getViewport(S32* viewport) -- cgit v1.2.3 From 799ebf21624edb8b42ca16b8cf51c138643efd32 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Fri, 10 May 2024 15:16:06 +0200 Subject: Fix broken merge and BOOL/bool issues --- indra/llrender/llshadermgr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 4e03882b7a..f649aa1963 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -188,7 +188,7 @@ bool LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) if (!shader->attachFragmentObject("deferred/globalF.glsl")) { - return FALSE; + return false; } if (features->hasSrgb || features->hasAtmospherics || features->calculatesAtmospherics || features->isDeferred) @@ -280,7 +280,7 @@ bool LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) { if (!shader->attachFragmentObject("deferred/pbrterrainUtilF.glsl")) { - return FALSE; + return false; } } -- cgit v1.2.3 From 9441608623a2692263191c254db23765eefa2cef Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Mon, 6 May 2024 10:07:02 -0700 Subject: secondlife/viewer#907: Local PBR terrain texture transforms --- indra/llrender/llshadermgr.cpp | 4 +++- indra/llrender/llshadermgr.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 4e03882b7a..21541c8bc8 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1179,7 +1179,9 @@ void LLShaderMgr::initAttribsAndUniforms() 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("terrain_texture_transforms"); // (GLTF) + + llassert(mReservedUniforms.size() == LLShaderMgr::TERRAIN_TEXTURE_TRANSFORMS +1); mReservedUniforms.push_back("viewport"); diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 2f344c8105..3dfac3cf0a 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -58,6 +58,8 @@ public: TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, // "texture_metallic_roughness_transform" (GLTF) TEXTURE_EMISSIVE_TRANSFORM, // "texture_emissive_transform" (GLTF) + TERRAIN_TEXTURE_TRANSFORMS, // "terrain_texture_transforms" (GLTF) + VIEWPORT, // "viewport" LIGHT_POSITION, // "light_position" LIGHT_DIRECTION, // "light_direction" -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/llrender/llcubemap.cpp | 674 ++-- indra/llrender/llcubemaparray.cpp | 370 +- indra/llrender/llcubemaparray.h | 152 +- indra/llrender/llfontbitmapcache.cpp | 372 +- indra/llrender/llfontbitmapcache.h | 162 +- indra/llrender/llfontfreetype.cpp | 1772 +++++----- indra/llrender/llfontfreetype.h | 386 +- indra/llrender/llfontfreetypesvg.h | 108 +- indra/llrender/llfontgl.cpp | 2640 +++++++------- indra/llrender/llfontgl.h | 496 +-- indra/llrender/llfontregistry.cpp | 1524 ++++---- indra/llrender/llgl.cpp | 5888 +++++++++++++++---------------- indra/llrender/llgl.h | 952 ++--- indra/llrender/llglslshader.cpp | 3904 ++++++++++---------- indra/llrender/llglslshader.h | 688 ++-- indra/llrender/llgltexture.cpp | 790 ++--- indra/llrender/llgltexture.h | 412 +-- indra/llrender/llimagegl.cpp | 5094 +++++++++++++------------- indra/llrender/llimagegl.h | 724 ++-- indra/llrender/llpostprocess.cpp | 908 ++--- indra/llrender/llrender.cpp | 4532 ++++++++++++------------ indra/llrender/llrender2dutils.cpp | 3164 ++++++++--------- indra/llrender/llrender2dutils.h | 354 +- indra/llrender/llshadermgr.cpp | 2846 +++++++-------- indra/llrender/llshadermgr.h | 694 ++-- indra/llrender/lltexturemanagerbridge.h | 94 +- indra/llrender/lluiimage.inl | 154 +- indra/llrender/llvertexbuffer.h | 552 +-- 28 files changed, 20203 insertions(+), 20203 deletions(-) (limited to 'indra/llrender') 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 >& 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 >& 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 >& 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 >& 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 >& 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 >& 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 - -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 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 + +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 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(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(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(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(EFontGlyphType::Count); idx < cnt; idx++) - { - for (LLImageGL* image_gl : mImageGLVec[idx]) - { - image_gl->destroyGLTexture(); - } - } -} - -void LLFontBitmapCache::reset() -{ - for (U32 idx = 0, cnt = static_cast(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(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(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(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(EFontGlyphType::Count); idx < cnt; idx++) + { + for (LLImageGL* image_gl : mImageGLVec[idx]) + { + image_gl->destroyGLTexture(); + } + } +} + +void LLFontBitmapCache::reset() +{ + for (U32 idx = 0, cnt = static_cast(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 -#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(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(EFontGlyphType::Count)] = { 1 }; - S32 mCurrentOffsetY[static_cast(EFontGlyphType::Count)] = { 1 }; - S32 mMaxCharWidth = 0; - S32 mMaxCharHeight = 0; - std::vector> mImageRawVec[static_cast(EFontGlyphType::Count)]; - std::vector> mImageGLVec[static_cast(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 +#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(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(EFontGlyphType::Count)] = { 1 }; + S32 mCurrentOffsetY[static_cast(EFontGlyphType::Count)] = { 1 }; + S32 mMaxCharWidth = 0; + S32 mMaxCharHeight = 0; + std::vector> mImageRawVec[static_cast(EFontGlyphType::Count)]; + std::vector> mImageGLVec[static_cast(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 -#ifdef LL_WINDOWS -#include -#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(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(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& 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 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 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 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 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 +#ifdef LL_WINDOWS +#include +#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(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(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& 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 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 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 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 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 -#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 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 char_functor_t; - void addFallbackFont(const LLPointer& 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, char_functor_t> fallback_font_t; - typedef std::vector 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 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 +#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 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 char_functor_t; + void addFallbackFont(const LLPointer& 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, char_functor_t> fallback_font_t; + typedef std::vector 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 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 -#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 +#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 - -#if LL_WINDOWS -#include -#include -#include -#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 > 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(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(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 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 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 + +#if LL_WINDOWS +#include +#include +#include +#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 > 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(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(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 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 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 > 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 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 > 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 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 -#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 +#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 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::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 sep(" "); - boost::tokenizer > tok(all_exts, sep); - for(boost::tokenizer >::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 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::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::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: - // .[.] [] - - 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: - // .[.] [] - - 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 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::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 sep(" "); + boost::tokenizer > tok(all_exts, sep); + for(boost::tokenizer >::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 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::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::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: + // .[.] [] + + 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: + // .[.] [] + + 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 -#include -#include - -#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 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 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 sGLQ; - - bool mInQ; - LLGLUpdate() - : mInQ(false) - { - } - virtual ~LLGLUpdate() - { - if (mInQ) - { - std::list::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 +#include +#include + +#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 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 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 sGLQ; + + bool mInQ; + LLGLUpdate() + : mInQ(false) + { + } + virtual ~LLGLUpdate() + { + if (mInQ) + { + std::list::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::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::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 sorted; - - for (std::set::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::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::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* attributes, - std::vector* 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 >::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* 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* 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* 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 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::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::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::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 sorted; + + for (std::set::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::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::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* attributes, + std::vector* 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 >::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* 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* 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* 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 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::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 - -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 - struct UniformSetting - { - S32 mUniform; - T mValue; - }; - - typedef UniformSetting IntSetting; - typedef UniformSetting FloatSetting; - typedef UniformSetting VectorSetting; - typedef UniformSetting 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 mIntegers; - std::vector mFloats; - std::vector mVectors; - std::vector 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 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* attributes, - std::vector* 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* attributes); - bool mapUniforms(const std::vector*); - void mapUniform(GLint index, const std::vector*); - 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 mAttribute; //lookup table of attribute enum to attribute channel -#else - std::vector 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 mUniform; //lookup table of uniform enum to uniform location - LLStaticStringTable mUniformMap; //lookup map of uniform name to uniform location - typedef std::unordered_map uniform_value_map_t; - uniform_value_map_t mValue; //lookup map of uniform location to last known value - std::vector 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 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 + +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 + struct UniformSetting + { + S32 mUniform; + T mValue; + }; + + typedef UniformSetting IntSetting; + typedef UniformSetting FloatSetting; + typedef UniformSetting VectorSetting; + typedef UniformSetting 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 mIntegers; + std::vector mFloats; + std::vector mVectors; + std::vector 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 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* attributes, + std::vector* 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* attributes); + bool mapUniforms(const std::vector*); + void mapUniform(GLint index, const std::vector*); + 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 mAttribute; //lookup table of attribute enum to attribute channel +#else + std::vector 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 mUniform; //lookup table of uniform enum to uniform location + LLStaticStringTable mUniformMap; //lookup map of uniform name to uniform location + typedef std::unordered_map uniform_value_map_t; + uniform_value_map_t mValue; //lookup map of uniform location to last known value + std::vector 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 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 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 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 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::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::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::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::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::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& dest, bool usemipmaps) -{ - dest = new LLImageGL(usemipmaps); - return true; -} - -//for server side use only. -bool LLImageGL::create(LLPointer& 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& 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 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(&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 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 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::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::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::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::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::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& dest, bool usemipmaps) +{ + dest = new LLImageGL(usemipmaps); + return true; +} + +//for server side use only. +bool LLImageGL::create(LLPointer& 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& 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 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(&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 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& dest, bool usemipmaps = true); - static bool create(LLPointer& dest, U32 width, U32 height, U8 components, bool usemipmaps = true); - static bool create(LLPointer& 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 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 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, 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 - bool post(CALLABLE&& func) - { - return getQueue().post(std::forward(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& dest, bool usemipmaps = true); + static bool create(LLPointer& dest, U32 width, U32 height, U8 components, bool usemipmaps = true); + static bool create(LLPointer& 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 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 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, 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 + bool post(CALLABLE&& func) + { + return getQueue().post(std::forward(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 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(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 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& texture, unsigned int width, unsigned int height) -{ - std::vector 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& texture) -{ - std::vector 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 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(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 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& texture, unsigned int width, unsigned int height) +{ + std::vector 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& texture) +{ + std::vector 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 vb; - std::chrono::steady_clock::time_point touched; -}; - -static std::unordered_map 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::iterator cache = sVBCache.find(vhash); - - LLPointer 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::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 vb; + std::chrono::steady_clock::time_point touched; +}; + +static std::unordered_map 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::iterator cache = sVBCache.find(vhash); + + LLPointer 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::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 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 LLRender2D::getUIImageByID(const LLUUID& image_id, S32 priority) -{ - if (mImageProvider) - { - return mImageProvider->getUIImageByID(image_id, priority); - } - else - { - return NULL; - } -} - -LLPointer 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 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 LLRender2D::getUIImageByID(const LLUUID& image_id, S32 priority) +{ + if (mImageProvider) + { + return mImageProvider->getUIImageByID(image_id, priority); + } + else + { + return NULL; + } +} + +LLPointer 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 -{ - 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 getUIImageByID(const LLUUID& image_id, S32 priority = 0); - LLPointer 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 getUIImage(const std::string& name, S32 priority) = 0; - virtual LLPointer 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 +{ + 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 getUIImageByID(const LLUUID& image_id, S32 priority = 0); + LLPointer 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 getUIImage(const std::string& name, S32 priority) = 0; + virtual LLPointer 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* 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 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 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 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* 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 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 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 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* 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 mVertexShaderObjects; - std::map mFragmentShaderObjects; - - //global (reserved slot) shader parameters - std::vector mReservedAttribs; - - std::vector mReservedUniforms; - - struct ProgramBinaryData - { - GLsizei mBinaryLength = 0; - GLenum mBinaryFormat = 0; - F32 mLastUsedTime = 0.0; - }; - std::map 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* 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 mVertexShaderObjects; + std::map mFragmentShaderObjects; + + //global (reserved slot) shader parameters + std::vector mReservedAttribs; + + std::vector mReservedUniforms; + + struct ProgramBinaryData + { + GLsizei mBinaryLength = 0; + GLenum mBinaryFormat = 0; + F32 mLastUsedTime = 0.0; + }; + std::map 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 getLocalTexture(bool usemipmaps = true, bool generate_gl_tex = true) = 0; - virtual LLPointer 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 getLocalTexture(bool usemipmaps = true, bool generate_gl_tex = true) = 0; + virtual LLPointer 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 -#include -#include - -#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& 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<getVertexBuffer(verts); - // vb->getNormalStrider(norms); - // setVertsNorms(verts, norms); - // vb->unmapBuffer(); - bool getVertexStrider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getVertexStrider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getIndexStrider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getTexCoord0Strider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getTexCoord1Strider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getTexCoord2Strider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getNormalStrider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getTangentStrider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getTangentStrider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getColorStrider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getEmissiveStrider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getWeightStrider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getWeight4Strider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getClothWeightStrider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getBasecolorTexcoordStrider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getNormalTexcoordStrider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getMetallicRoughnessTexcoordStrider(LLStrider& strider, U32 index=0, S32 count = -1); - bool getEmissiveTexcoordStrider(LLStrider& 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 mMappedVertexRegions; // list of mMappedData byte ranges that must be sent to GL - std::vector 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 +#include +#include + +#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& 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<getVertexBuffer(verts); + // vb->getNormalStrider(norms); + // setVertsNorms(verts, norms); + // vb->unmapBuffer(); + bool getVertexStrider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getVertexStrider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getIndexStrider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getTexCoord0Strider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getTexCoord1Strider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getTexCoord2Strider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getNormalStrider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getTangentStrider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getTangentStrider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getColorStrider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getEmissiveStrider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getWeightStrider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getWeight4Strider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getClothWeightStrider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getBasecolorTexcoordStrider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getNormalTexcoordStrider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getMetallicRoughnessTexcoordStrider(LLStrider& strider, U32 index=0, S32 count = -1); + bool getEmissiveTexcoordStrider(LLStrider& 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 mMappedVertexRegions; // list of mMappedData byte ranges that must be sent to GL + std::vector 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 -- cgit v1.2.3 From db627bc3544b828982a2a366972e0adf86e42f75 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 28 May 2024 14:37:38 +0300 Subject: viewer#1553 Local PBR materials are not applied to the terrain --- indra/llrender/llgltexture.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'indra/llrender') diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp index 88c061c56d..e614f45986 100644 --- a/indra/llrender/llgltexture.cpp +++ b/indra/llrender/llgltexture.cpp @@ -49,6 +49,10 @@ LLGLTexture::LLGLTexture(const LLImageRaw* raw, bool usemipmaps) mUseMipMaps = usemipmaps ; // Create an empty image of the specified size and width mGLTexturep = new LLImageGL(raw, usemipmaps) ; + mFullWidth = mGLTexturep->getWidth(); + mFullHeight = mGLTexturep->getHeight(); + mComponents = mGLTexturep->getComponents(); + setTexelsPerImage(); } LLGLTexture::~LLGLTexture() -- cgit v1.2.3 From 2f4120038429c6aff865f153f708ceefb60d67f4 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Tue, 28 May 2024 09:45:40 -0500 Subject: Remove tinygltf dependency from LL::GLTF (#1541) * #1535 Image loading/saving support in boost::json driven GLTF parser * #1536 GLB Support in boost::json drvien GLTF parser --- indra/llrender/llglslshader.cpp | 6 ++++++ indra/llrender/llglslshader.h | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index f6b1ca3741..ecd0c6908b 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -1049,6 +1049,12 @@ void LLGLSLShader::bind() } } +void LLGLSLShader::bind(LLGLSLShader::GLTFVariant variant) +{ + llassert(mGLTFVariants.size() == LLGLSLShader::NUM_GLTF_VARIANTS); + mGLTFVariants[variant].bind(); +} + void LLGLSLShader::bind(bool rigged) { if (rigged) diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 75e1bdfd54..fa01d212e1 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -44,6 +44,7 @@ public: bool hasTransport = false; // implies no lighting (it's possible to have neither though) bool hasSkinning = false; bool hasObjectSkinning = false; + bool mGLTF = false; bool hasAtmospherics = false; bool hasGamma = false; bool hasShadows = false; @@ -318,6 +319,22 @@ public: // this pointer should be set to whichever shader represents this shader's rigged variant LLGLSLShader* mRiggedVariant = nullptr; + // variants for use by GLTF renderer + // "this" is considered to be OPAQUE + enum GLTFVariant + { + STATIC_OPAQUE, + STATIC_BLEND, + RIGGED_OPAQUE, + RIGGED_BLEND, + NUM_GLTF_VARIANTS + }; + + std::vector mGLTFVariants; + + //helper to bind GLTF variant + void bind(GLTFVariant variant); + // hacky flag used for optimization in LLDrawPoolAlpha bool mCanBindFast = false; -- cgit v1.2.3 From 15fd13f83036ff781160957a21bb2d59771044bc Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 29 May 2024 16:56:39 -0500 Subject: #1530 Increase joint limit for GLTF Assets (#1582) * Migrate GLTF scene rendering to its own shaders * Add support for ambient occlusion map separate from metallic roughness map (or absent) * Use UBO's for GLTF joints * Better error handling of downloading GLTF assets --- indra/llrender/llgl.cpp | 5 +++++ indra/llrender/llgl.h | 1 + indra/llrender/llglslshader.cpp | 25 +++++++++++++++++-------- indra/llrender/llglslshader.h | 28 ++++++++++++++++++++-------- indra/llrender/llshadermgr.cpp | 4 +++- indra/llrender/llshadermgr.h | 4 +++- indra/llrender/llvertexbuffer.cpp | 5 +++-- 7 files changed, 52 insertions(+), 20 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index ac697b72be..0586c34e50 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -1238,6 +1238,11 @@ bool LLGLManager::initGL() glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &mMaxIntegerSamples); glGetIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &mMaxSampleMaskWords); glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples); + glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &mMaxUniformBlockSize); + + // sanity clamp max uniform block size to 64k just in case + // there's some implementation that reports a crazy value + mMaxUniformBlockSize = llmin(mMaxUniformBlockSize, 65536); if (mGLVersion >= 4.59f) { diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 75a7c5d3b2..2f538d0844 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -87,6 +87,7 @@ public: S32 mGLMaxIndexRange; S32 mGLMaxTextureSize; F32 mMaxAnisotropy = 0.f; + S32 mMaxUniformBlockSize = 0; // GL 4.x capabilities bool mHasCubeMapArray = false; diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index ecd0c6908b..8ea134393a 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -983,17 +983,25 @@ bool LLGLSLShader::mapUniforms(const vector* 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"); + // 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 + const char* ubo_names[] = + { + "ReflectionProbes", // UB_REFLECTION_PROBES + "GLTFJoints", // UB_GLTF_JOINTS + }; + + llassert(LL_ARRAY_SIZE(ubo_names) == NUM_UNIFORM_BLOCKS); + + for (U32 i = 0; i < NUM_UNIFORM_BLOCKS; ++i) + { + GLuint UBOBlockIndex = glGetUniformBlockIndex(mProgramObject, ubo_names[i]); if (UBOBlockIndex != GL_INVALID_INDEX) { - //Set this index to a binding index - glUniformBlockBinding(mProgramObject, UBOBlockIndex, BLOCKBINDING); + glUniformBlockBinding(mProgramObject, UBOBlockIndex, i); } } + unbind(); LL_DEBUGS("ShaderUniform") << "Total Uniform Size: " << mTotalUniformSize << LL_ENDL; @@ -1049,9 +1057,10 @@ void LLGLSLShader::bind() } } -void LLGLSLShader::bind(LLGLSLShader::GLTFVariant variant) +void LLGLSLShader::bind(U32 variant) { llassert(mGLTFVariants.size() == LLGLSLShader::NUM_GLTF_VARIANTS); + llassert(variant < LLGLSLShader::NUM_GLTF_VARIANTS); mGLTFVariants[variant].bind(); } diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index fa01d212e1..8ebea2deca 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -146,6 +146,14 @@ public: SG_COUNT } eGroup; + enum UniformBlock : GLuint + { + UB_REFLECTION_PROBES, + UB_GLTF_JOINTS, + NUM_UNIFORM_BLOCKS + }; + + static std::set sInstances; static bool sProfileEnabled; @@ -320,20 +328,24 @@ public: LLGLSLShader* mRiggedVariant = nullptr; // variants for use by GLTF renderer - // "this" is considered to be OPAQUE - enum GLTFVariant + // bit 0 = alpha mode blend (1) or opaque (0) + // bit 1 = rigged (1) or static (0) + struct GLTFVariant { - STATIC_OPAQUE, - STATIC_BLEND, - RIGGED_OPAQUE, - RIGGED_BLEND, - NUM_GLTF_VARIANTS + constexpr static U32 RIGGED = 2; + constexpr static U32 ALPHA = 1; + constexpr static U32 OPAQUE_STATIC = 0; + constexpr static U32 ALPHA_STATIC = 1; + constexpr static U32 OPAQUE_RIGGED = 2; + constexpr static U32 ALPHA_RIGGED = 3; }; + constexpr static U32 NUM_GLTF_VARIANTS = 4; + std::vector mGLTFVariants; //helper to bind GLTF variant - void bind(GLTFVariant variant); + void bind(U32 variant); // hacky flag used for optimization in LLDrawPoolAlpha bool mCanBindFast = false; diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 694bcbeeb9..4e8adb2fb3 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1225,6 +1225,9 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("diffuseMap"); mReservedUniforms.push_back("altDiffuseMap"); mReservedUniforms.push_back("specularMap"); + mReservedUniforms.push_back("metallicRoughnessMap"); + mReservedUniforms.push_back("normalMap"); + mReservedUniforms.push_back("occlusionMap"); mReservedUniforms.push_back("emissiveMap"); mReservedUniforms.push_back("bumpMap"); mReservedUniforms.push_back("bumpMap2"); @@ -1348,7 +1351,6 @@ void LLShaderMgr::initAttribsAndUniforms() llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_SHADOW5+1); - mReservedUniforms.push_back("normalMap"); mReservedUniforms.push_back("positionMap"); mReservedUniforms.push_back("diffuseRect"); mReservedUniforms.push_back("specularRect"); diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index c3e5a2aafd..03803c0e96 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -93,6 +93,9 @@ public: DIFFUSE_MAP, // "diffuseMap" ALTERNATE_DIFFUSE_MAP, // "altDiffuseMap" SPECULAR_MAP, // "specularMap" + METALLIC_ROUGHNESS_MAP, // "metallicRoughnessMap" + NORMAL_MAP, // "normalMap" + OCCLUSION_MAP, // "occlusionMap" EMISSIVE_MAP, // "emissiveMap" BUMP_MAP, // "bumpMap" BUMP_MAP2, // "bumpMap2" @@ -202,7 +205,6 @@ public: DEFERRED_SHADOW3, // "shadowMap3" DEFERRED_SHADOW4, // "shadowMap4" DEFERRED_SHADOW5, // "shadowMap5" - DEFERRED_NORMAL, // "normalMap" DEFERRED_POSITION, // "positionMap" DEFERRED_DIFFUSE, // "diffuseRect" DEFERRED_SPECULAR, // "specularRect" diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 7caf20f40b..8cb124d406 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -687,6 +687,7 @@ bool LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_of } { +#if 0 // not a reliable test for VBOs that are not backed by a CPU buffer U16* idx = (U16*) mMappedIndexData+indices_offset; for (U32 i = 0; i < count; ++i) { @@ -724,6 +725,7 @@ bool LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_of } } } +#endif } return true; @@ -1036,8 +1038,7 @@ bool LLVertexBuffer::updateNumIndices(U32 nindices) bool LLVertexBuffer::allocateBuffer(U32 nverts, U32 nindices) { - if (nverts < 0 || nindices < 0 || - nverts > 65536) + if (nverts < 0 || nindices < 0) { LL_ERRS() << "Bad vertex buffer allocation: " << nverts << " : " << nindices << LL_ENDL; } -- cgit v1.2.3 From cb3bd8865aa0f9fb8a247ea595cf1973057ba91f Mon Sep 17 00:00:00 2001 From: Ansariel Date: Thu, 30 May 2024 15:41:36 +0200 Subject: Fix a bunch of uninitialized variable warnings that showed up in Visual Studio --- indra/llrender/llglslshader.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 95e8f1168a..698da92877 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -68,8 +68,8 @@ public: template struct UniformSetting { - S32 mUniform; - T mValue; + S32 mUniform{ 0 }; + T mValue{}; }; typedef UniformSetting IntSetting; -- cgit v1.2.3 From b42f9d836b4c0f7fbd4bdae1734021e2a09fdbe8 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 1 Jun 2024 15:49:26 +0200 Subject: Re-enable a lot of compiler warnings for MSVC and address the C4267 "possible loss of precision" warnings --- indra/llrender/llfontbitmapcache.cpp | 2 +- indra/llrender/llfontbitmapcache.h | 2 +- indra/llrender/llfontfreetype.cpp | 2 +- indra/llrender/llfontgl.cpp | 2 +- indra/llrender/llglslshader.cpp | 4 ++-- indra/llrender/llrendertarget.cpp | 10 +++++----- indra/llrender/llshadermgr.cpp | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp index d2c40ad590..46c2e89797 100644 --- a/indra/llrender/llfontbitmapcache.cpp +++ b/indra/llrender/llfontbitmapcache.cpp @@ -108,7 +108,7 @@ bool LLFontBitmapCache::nextOpenPos(S32 width, S32& pos_x, S32& pos_y, EFontGlyp S32 num_components = getNumComponents(bitmap_type); mImageRawVec[bitmap_idx].push_back(new LLImageRaw(mBitmapWidth, mBitmapHeight, num_components)); - bitmap_num = mImageRawVec[bitmap_idx].size() - 1; + bitmap_num = static_cast(mImageRawVec[bitmap_idx].size()) - 1; LLImageRaw* image_raw = getImageRaw(bitmap_type, bitmap_num); if (EFontGlyphType::Grayscale == bitmap_type) diff --git a/indra/llrender/llfontbitmapcache.h b/indra/llrender/llfontbitmapcache.h index 6b19ba2154..f2dfd87877 100644 --- a/indra/llrender/llfontbitmapcache.h +++ b/indra/llrender/llfontbitmapcache.h @@ -60,7 +60,7 @@ public: LLImageGL* getImageGL(EFontGlyphType bitmapType, U32 bitmapNum) const; S32 getMaxCharWidth() const { return mMaxCharWidth; } - U32 getNumBitmaps(EFontGlyphType bitmapType) const { return (bitmapType < EFontGlyphType::Count) ? mImageRawVec[static_cast(bitmapType)].size() : 0; } + U32 getNumBitmaps(EFontGlyphType bitmapType) const { return (bitmapType < EFontGlyphType::Count) ? static_cast(mImageRawVec[static_cast(bitmapType)].size()) : 0U; } S32 getBitmapWidth() const { return mBitmapWidth; } S32 getBitmapHeight() const { return mBitmapHeight; } diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp index 94b576885f..741ed993b0 100644 --- a/indra/llrender/llfontfreetype.cpp +++ b/indra/llrender/llfontfreetype.cpp @@ -328,7 +328,7 @@ S32 LLFontFreetype::ftOpenFace(const std::string& filename, S32 face_n) pFtStream = new LLFT_Stream(); pFtStream->base = 0; pFtStream->pos = 0; - pFtStream->size = file_size; + pFtStream->size = static_cast(file_size); pFtStream->descriptor.pointer = pFileStream; pFtStream->read = ft_read_cb; pFtStream->close = ft_close_cb; diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 25706c77eb..7e10a4e159 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -149,7 +149,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons if(!sDisplayFont) //do not display texts { - return wstr.length() ; + return static_cast(wstr.length()); } if (wstr.empty()) diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 1651835b59..2caeb1c431 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -621,7 +621,7 @@ bool LLGLSLShader::mapAttributes(const std::vector* attrib } mAttribute.clear(); - U32 numAttributes = (attributes == NULL) ? 0 : attributes->size(); + U32 numAttributes = (attributes == NULL) ? 0U : static_cast(attributes->size()); #if LL_RELEASE_WITH_DEBUG_INFO mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, { -1, NULL }); #else @@ -843,7 +843,7 @@ bool LLGLSLShader::mapUniforms(const vector* uniforms) mTexture.clear(); mValue.clear(); //initialize arrays - U32 numUniforms = (uniforms == NULL) ? 0 : uniforms->size(); + U32 numUniforms = (uniforms == NULL) ? 0U : static_cast(uniforms->size()); mUniform.resize(numUniforms + LLShaderMgr::instance()->mReservedUniforms.size(), -1); mTexture.resize(numUniforms + LLShaderMgr::instance()->mReservedUniforms.size(), -1); diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 8c05a47c39..bd64ba84fb 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -208,7 +208,7 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt) return true; } - U32 offset = mTex.size(); + U32 offset = static_cast(mTex.size()); if( offset >= 4 ) { @@ -378,11 +378,11 @@ void LLRenderTarget::release() if (mFBO && (mTex.size() > 1)) { glBindFramebuffer(GL_FRAMEBUFFER, mFBO); - S32 z; + size_t z; for (z = mTex.size() - 1; z >= 1; z--) { sBytesAllocated -= mResX*mResY*4; - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+z, LLTexUnit::getInternalType(mUsage), 0, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, static_cast(GL_COLOR_ATTACHMENT0+z), LLTexUnit::getInternalType(mUsage), 0, 0); LLImageGL::deleteTextures(1, &mTex[z]); } glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); @@ -426,7 +426,7 @@ void LLRenderTarget::bindTarget() GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3}; - glDrawBuffers(mTex.size(), drawbuffers); + glDrawBuffers(static_cast(mTex.size()), drawbuffers); if (mTex.empty()) { //no color buffer to draw to @@ -485,7 +485,7 @@ U32 LLRenderTarget::getTexture(U32 attachment) const U32 LLRenderTarget::getNumTextures() const { - return mTex.size(); + return static_cast(mTex.size()); } void LLRenderTarget::bindTexture(U32 index, S32 channel, LLTexUnit::eTextureFilterOptions filter_options) diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 98bfb38f03..6ac2b62777 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1096,7 +1096,7 @@ bool LLShaderMgr::saveCachedProgramBinary(LLGLSLShader* shader) 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()); + glGetProgramBinary(shader->mProgramObject, static_cast(program_binary.size() * sizeof(U8)), nullptr, &binary_info.mBinaryFormat, program_binary.data()); error = glGetError(); if (error == GL_NO_ERROR) { -- cgit v1.2.3 From 24586f810eb7ef8048a55687333d51c53aa2bed8 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 5 Jun 2024 15:14:13 -0500 Subject: #1527 Improve performance on Apple silicon (#1632) --- indra/llrender/llgl.h | 15 ++- indra/llrender/llrender.cpp | 8 +- indra/llrender/llvertexbuffer.cpp | 187 +++++++++++++++++++++++++++++++++----- 3 files changed, 181 insertions(+), 29 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 2f538d0844..254c983110 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -156,13 +156,18 @@ 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 + +// stop_glerror is still needed on OS X but has performance implications +// use macro below to conditionally add stop_glerror to non-release builds +// on OS X +#if LL_DARWIN && !LL_RELEASE_FOR_DOWNLOAD +#define STOP_GLERROR stop_glerror() +#else +#define STOP_GLERROR +#endif #define llglassertok_always() assert_glerror() diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index 399281be84..51028e5667 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -990,6 +990,7 @@ void LLRender::syncLightState() void LLRender::syncMatrices() { + STOP_GLERROR; static const U32 name[] = { LLShaderMgr::MODELVIEW_MATRIX, @@ -1012,8 +1013,6 @@ void LLRender::syncMatrices() if (shader) { - //llassert(shader); - bool mvp_done = false; U32 i = MM_MODELVIEW; @@ -1134,6 +1133,7 @@ void LLRender::syncMatrices() syncLightState(); } } + STOP_GLERROR; } void LLRender::translatef(const GLfloat& x, const GLfloat& y, const GLfloat& z) @@ -1585,6 +1585,7 @@ void LLRender::end() } void LLRender::flush() { + STOP_GLERROR; if (mCount > 0) { LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE; @@ -1693,6 +1694,9 @@ void LLRender::flush() vb->setColorData(mColorsp.get()); } +#if LL_DARWIN + vb->unmapBuffer(); +#endif vb->unbind(); sVBCache[vhash] = { vb , std::chrono::steady_clock::now() }; diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 8cb124d406..fa3b2df6e0 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -290,6 +290,62 @@ static GLuint gen_buffer() #define ANALYZE_VBO_POOL 0 +#if LL_DARWIN + +// experimental -- disable VBO pooling on OS X and use glMapBuffer +class LLVBOPool +{ +public: + U64 mAllocated = 0; + + U64 getVramBytesUsed() + { + return mAllocated; + } + + void allocate(GLenum type, U32 size, GLuint& name, U8*& data) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VERTEX; + STOP_GLERROR; + llassert(type == GL_ARRAY_BUFFER || type == GL_ELEMENT_ARRAY_BUFFER); + llassert(name == 0); // non zero name indicates a gl name that wasn't freed + llassert(data == nullptr); // non null data indicates a buffer that wasn't freed + llassert(size >= 2); // any buffer size smaller than a single index is nonsensical + + mAllocated += size; + + { //allocate a new buffer + LL_PROFILE_GPU_ZONE("vbo alloc"); + // ON OS X, we don't allocate a VBO until the last possible moment + // in unmapBuffer + data = (U8*) ll_aligned_malloc_16(size); + STOP_GLERROR; + } + } + + void free(GLenum type, U32 size, GLuint name, U8* data) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VERTEX; + llassert(type == GL_ARRAY_BUFFER || type == GL_ELEMENT_ARRAY_BUFFER); + llassert(size >= 2); + + if (data) + { + ll_aligned_free_16(data); + } + + mAllocated -= size; + STOP_GLERROR; + if (name) + { + glDeleteBuffers(1, &name); + } + STOP_GLERROR; + } +}; + +#else + class LLVBOPool { public: @@ -509,9 +565,8 @@ public: mIBOPool.clear(); mVBOPool.clear(); } - - }; +#endif static LLVBOPool* sVBOPool = nullptr; @@ -629,6 +684,8 @@ void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVecto LL_PROFILE_ZONE_SCOPED_CATEGORY_VERTEX; llassert(LLGLSLShader::sCurBoundShaderPtr != NULL); + STOP_GLERROR; + gGL.syncMatrices(); U32 mask = LLVertexBuffer::MAP_VERTEX; @@ -743,8 +800,10 @@ void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indi llassert(mGLBuffer == sGLRenderBuffer); llassert(mGLIndices == sGLRenderIndices); gGL.syncMatrices(); + STOP_GLERROR; glDrawRangeElements(sGLMode[mode], start, end, count, mIndicesType, (GLvoid*) (indices_offset * (size_t) mIndicesStride)); + STOP_GLERROR; } void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const @@ -760,7 +819,9 @@ void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const llassert(mGLIndices == sGLRenderIndices); gGL.syncMatrices(); + STOP_GLERROR; glDrawArrays(sGLMode[mode], first, count); + STOP_GLERROR; } //static @@ -783,9 +844,10 @@ void LLVertexBuffer::initClass(LLWindow* window) //static void LLVertexBuffer::unbind() { + STOP_GLERROR; glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - + STOP_GLERROR; sGLRenderBuffer = 0; sGLRenderIndices = 0; } @@ -1081,6 +1143,7 @@ U8* LLVertexBuffer::mapVertexBuffer(LLVertexBuffer::AttributeType type, U32 inde count = mNumVerts - index; } +#if !LL_DARWIN U32 start = mOffsets[type] + sTypeSize[type] * index; U32 end = start + sTypeSize[type] * count-1; @@ -1101,7 +1164,7 @@ U8* LLVertexBuffer::mapVertexBuffer(LLVertexBuffer::AttributeType type, U32 inde //didn't expand an existing region, make a new one mMappedVertexRegions.push_back({ start, end }); } - +#endif return mMappedData+mOffsets[type]+sTypeSize[type]*index; } @@ -1115,6 +1178,7 @@ U8* LLVertexBuffer::mapIndexBuffer(U32 index, S32 count) count = mNumIndices-index; } +#if !LL_DARWIN U32 start = sizeof(U16) * index; U32 end = start + sizeof(U16) * count-1; @@ -1135,6 +1199,7 @@ U8* LLVertexBuffer::mapIndexBuffer(U32 index, S32 count) //didn't expand an existing region, make a new one mMappedIndexRegions.push_back({ start, end }); } +#endif return mMappedIndexData + sizeof(U16)*index; } @@ -1143,9 +1208,17 @@ U8* LLVertexBuffer::mapIndexBuffer(U32 index, S32 count) // target -- "target" parameter for glBufferSubData // start -- first byte to copy // end -- last byte to copy (NOT last byte + 1) -// data -- mMappedData or mMappedIndexData -static void flush_vbo(GLenum target, U32 start, U32 end, void* data) -{ +// data -- data to be flushed +// dst -- mMappedData or mMappedIndexData +static void flush_vbo(GLenum target, U32 start, U32 end, void* data, U8* dst) +{ +#if LL_DARWIN + LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb memcpy"); + STOP_GLERROR; + // copy into mapped buffer + memcpy(dst+start, data, end-start+1); +#else + // skip mapped data and stream to GPU via glBufferSubData if (end != 0) { LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("glBufferSubData"); @@ -1164,10 +1237,12 @@ static void flush_vbo(GLenum target, U32 start, U32 end, void* data) glBufferSubData(target, i, size, (U8*) data + (i-start)); } } +#endif } void LLVertexBuffer::unmapBuffer() { + STOP_GLERROR; struct SortMappedRegion { bool operator()(const MappedRegion& lhs, const MappedRegion& rhs) @@ -1176,9 +1251,51 @@ void LLVertexBuffer::unmapBuffer() } }; +#if LL_DARWIN + STOP_GLERROR; + if (mMappedData) + { + if (mGLBuffer) + { + glDeleteBuffers(1, &mGLBuffer); + } + mGLBuffer = gen_buffer(); + glBindBuffer(GL_ARRAY_BUFFER, mGLBuffer); + sGLRenderBuffer = mGLBuffer; + glBufferData(GL_ARRAY_BUFFER, mSize, mMappedData, GL_STATIC_DRAW); + } + else if (mGLBuffer != sGLRenderBuffer) + { + glBindBuffer(GL_ARRAY_BUFFER, mGLBuffer); + sGLRenderBuffer = mGLBuffer; + } + STOP_GLERROR; + + if (mMappedIndexData) + { + if (mGLIndices) + { + glDeleteBuffers(1, &mGLIndices); + } + + mGLIndices = gen_buffer(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mGLIndices); + sGLRenderIndices = mGLIndices; + + glBufferData(GL_ELEMENT_ARRAY_BUFFER, mIndicesSize, mMappedIndexData, GL_STATIC_DRAW); + } + else if (mGLIndices != sGLRenderIndices) + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mGLIndices); + sGLRenderIndices = mGLIndices; + } + STOP_GLERROR; +#else + if (!mMappedVertexRegions.empty()) { LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("unmapBuffer - vertex"); + if (sGLRenderBuffer != mGLBuffer) { glBindBuffer(GL_ARRAY_BUFFER, mGLBuffer); @@ -1199,14 +1316,13 @@ void LLVertexBuffer::unmapBuffer() } else { - flush_vbo(GL_ARRAY_BUFFER, start, end, (U8*)mMappedData + start); + flush_vbo(GL_ARRAY_BUFFER, start, end, (U8*)mMappedData + start, mMappedData); start = region.mStart; end = region.mEnd; } } - flush_vbo(GL_ARRAY_BUFFER, start, end, (U8*)mMappedData + start); - + flush_vbo(GL_ARRAY_BUFFER, start, end, (U8*)mMappedData + start, mMappedData); mMappedVertexRegions.clear(); } @@ -1233,16 +1349,16 @@ void LLVertexBuffer::unmapBuffer() } else { - flush_vbo(GL_ELEMENT_ARRAY_BUFFER, start, end, (U8*)mMappedIndexData + start); + flush_vbo(GL_ELEMENT_ARRAY_BUFFER, start, end, (U8*)mMappedIndexData + start, mMappedIndexData); start = region.mStart; end = region.mEnd; } } - flush_vbo(GL_ELEMENT_ARRAY_BUFFER, start, end, (U8*)mMappedIndexData + start); - + flush_vbo(GL_ELEMENT_ARRAY_BUFFER, start, end, (U8*)mMappedIndexData + start, mMappedIndexData); mMappedIndexRegions.clear(); } +#endif } //---------------------------------------------------------------------------- @@ -1363,10 +1479,17 @@ bool LLVertexBuffer::getClothWeightStrider(LLStrider& strider, U32 in // Set for rendering void LLVertexBuffer::setBuffer() { + STOP_GLERROR; +#if LL_DARWIN + if (!mGLBuffer) + { // OS X doesn't allocate a buffer until we call unmapBuffer + return; + } +#endif // no data may be pending llassert(mMappedVertexRegions.empty()); llassert(mMappedIndexRegions.empty()); - + // a shader must be bound llassert(LLGLSLShader::sCurBoundShaderPtr); @@ -1395,12 +1518,15 @@ void LLVertexBuffer::setBuffer() glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mGLIndices); sGLRenderIndices = mGLIndices; } + + STOP_GLERROR; } // virtual (default) void LLVertexBuffer::setupVertexBuffer() { + STOP_GLERROR; U8* base = nullptr; U32 data_mask = LLGLSLShader::sCurBoundShaderPtr->mAttributeMask; @@ -1490,59 +1616,76 @@ void LLVertexBuffer::setupVertexBuffer() void* ptr = (void*)(base + mOffsets[TYPE_VERTEX]); glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_VERTEX], ptr); } + STOP_GLERROR; } void LLVertexBuffer::setPositionData(const LLVector4a* data) { +#if !LL_DARWIN llassert(sGLRenderBuffer == mGLBuffer); - flush_vbo(GL_ARRAY_BUFFER, 0, sizeof(LLVector4a) * getNumVerts()-1, (U8*) data); +#endif + flush_vbo(GL_ARRAY_BUFFER, 0, sizeof(LLVector4a) * getNumVerts()-1, (U8*) data, mMappedData); } void LLVertexBuffer::setTexCoordData(const LLVector2* data) { +#if !LL_DARWIN llassert(sGLRenderBuffer == mGLBuffer); - flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD0], mOffsets[TYPE_TEXCOORD0] + sTypeSize[TYPE_TEXCOORD0] * getNumVerts() - 1, (U8*)data); +#endif + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD0], mOffsets[TYPE_TEXCOORD0] + sTypeSize[TYPE_TEXCOORD0] * getNumVerts() - 1, (U8*)data, mMappedData); } void LLVertexBuffer::setColorData(const LLColor4U* data) { +#if !LL_DARWIN llassert(sGLRenderBuffer == mGLBuffer); - flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_COLOR], mOffsets[TYPE_COLOR] + sTypeSize[TYPE_COLOR] * getNumVerts() - 1, (U8*) data); +#endif + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_COLOR], mOffsets[TYPE_COLOR] + sTypeSize[TYPE_COLOR] * getNumVerts() - 1, (U8*) data, mMappedData); } void LLVertexBuffer::setNormalData(const LLVector4a* data) { +#if !LL_DARWIN llassert(sGLRenderBuffer == mGLBuffer); - flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_NORMAL], mOffsets[TYPE_NORMAL] + sTypeSize[TYPE_NORMAL] * getNumVerts() - 1, (U8*) data); +#endif + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_NORMAL], mOffsets[TYPE_NORMAL] + sTypeSize[TYPE_NORMAL] * getNumVerts() - 1, (U8*) data, mMappedData); } void LLVertexBuffer::setTangentData(const LLVector4a* data) { +#if !LL_DARWIN llassert(sGLRenderBuffer == mGLBuffer); - flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TANGENT], mOffsets[TYPE_TANGENT] + sTypeSize[TYPE_TANGENT] * getNumVerts() - 1, (U8*) data); +#endif + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TANGENT], mOffsets[TYPE_TANGENT] + sTypeSize[TYPE_TANGENT] * getNumVerts() - 1, (U8*) data, mMappedData); } void LLVertexBuffer::setWeight4Data(const LLVector4a* data) { +#if !LL_DARWIN llassert(sGLRenderBuffer == mGLBuffer); - flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_WEIGHT4], mOffsets[TYPE_WEIGHT4] + sTypeSize[TYPE_WEIGHT4] * getNumVerts() - 1, (U8*) data); +#endif + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_WEIGHT4], mOffsets[TYPE_WEIGHT4] + sTypeSize[TYPE_WEIGHT4] * getNumVerts() - 1, (U8*) data, mMappedData); } void LLVertexBuffer::setIndexData(const U16* data) { +#if !LL_DARWIN llassert(sGLRenderIndices == mGLIndices); - flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U16) * getNumIndices() - 1, (U8*) data); +#endif + flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U16) * getNumIndices() - 1, (U8*) data, mMappedIndexData); } void LLVertexBuffer::setIndexData(const U32* data) { +#if !LL_DARWIN llassert(sGLRenderIndices == mGLIndices); +#endif if (mIndicesType != GL_UNSIGNED_INT) { // HACK -- vertex buffers are initialized as 16-bit indices, but can be switched to 32-bit indices mIndicesType = GL_UNSIGNED_INT; mIndicesStride = 4; mNumIndices /= 2; } - flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U32) * getNumIndices() - 1, (U8*)data); + flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U32) * getNumIndices() - 1, (U8*)data, mMappedIndexData); } -- cgit v1.2.3 From c0fad3028fd55c2067ce6a0ae4382cffe1014284 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 10 Jun 2024 16:42:43 +0200 Subject: Re-enable compiler warnings C4018, C4100, C4231 and C4506 --- indra/llrender/llgl.cpp | 6 +++--- indra/llrender/llgl.h | 5 +---- indra/llrender/llimagegl.cpp | 4 ++-- indra/llrender/llshadermgr.cpp | 2 +- indra/llrender/llvertexbuffer.cpp | 20 +++++++++----------- 5 files changed, 16 insertions(+), 21 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index ac697b72be..7b9d24c1e9 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -1173,7 +1173,7 @@ bool LLGLManager::initGL() // This is called here because it depends on the setting of mIsGF2or4MX, and sets up mHasMultitexture. initExtensions(); - S32 old_vram = mVRAM; + U32 old_vram = mVRAM; mVRAM = 0; #if LL_WINDOWS @@ -1215,7 +1215,7 @@ bool LLGLManager::initGL() // 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(); + U32 mem = LLDXHardware::getMBVideoMemoryViaWMI(); if (mem != 0) { mVRAM = mem; @@ -1345,7 +1345,7 @@ void LLGLManager::asLLSD(LLSD& info) info["gpu_version"] = mDriverVersionVendorString; info["opengl_version"] = mGLVersionString; - info["vram"] = mVRAM; + info["vram"] = LLSD::Integer(mVRAM); // OpenGL limits info["max_samples"] = mMaxSamples; diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 75a7c5d3b2..c9130545c1 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -118,9 +118,7 @@ public: std::string mDriverVersionVendorString; std::string mGLVersionString; - S32 mVRAM; // VRAM in MB - - void getPixelFormat(); // Get the best pixel format + U32 mVRAM; // VRAM in MB std::string getGLInfoString(); void printGLInfoString(); @@ -138,7 +136,6 @@ public: private: void initExtensions(); void initGLStates(); - void initGLImages(); }; extern LLGLManager gGLManager; diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 59f3dd56d6..7e5cd628c1 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -100,7 +100,7 @@ void LLImageGLMemory::free_tex_image(U32 texName) // track texture free on given texNames void LLImageGLMemory::free_tex_images(U32 count, const U32* texNames) { - for (int i = 0; i < count; ++i) + for (U32 i = 0; i < count; ++i) { free_tex_image(texNames[i]); } @@ -1318,7 +1318,7 @@ void LLImageGL::generateTextures(S32 numTextures, U32 *textures) name_count = pool_size; } - if (numTextures <= name_count) + if ((U32)numTextures <= name_count) { //copy teture names off the end of the pool memcpy(textures, name_pool + name_count - numTextures, sizeof(U32) * numTextures); diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 6ac2b62777..0f08466c33 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -665,7 +665,7 @@ GLuint LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_lev { //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) + for (S32 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()); diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index c98de6bf06..4efd2ce887 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -643,7 +643,7 @@ void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVecto if (tc != nullptr) { - for (int i = 0; i < num_indices; ++i) + for (U32 i = 0; i < num_indices; ++i) { U16 idx = indicesp[i]; gGL.texCoord2fv(tc[idx].mV); @@ -652,7 +652,7 @@ void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVecto } else { - for (int i = 0; i < num_indices; ++i) + for (U32 i = 0; i < num_indices; ++i) { U16 idx = indicesp[i]; gGL.vertex3fv(pos[idx].getF32ptr()); @@ -669,19 +669,17 @@ bool LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_of return true; } - llassert(start < (U32)mNumVerts); - llassert(end < (U32)mNumVerts); + llassert(start < mNumVerts); + llassert(end < mNumVerts); - if (start >= (U32) mNumVerts || - end >= (U32) mNumVerts) + if (start >= mNumVerts || + end >= mNumVerts) { LL_ERRS() << "Bad vertex buffer draw range: [" << start << ", " << end << "] vs " << mNumVerts << LL_ENDL; } - llassert(mNumIndices >= 0); - - if (indices_offset >= (U32) mNumIndices || - indices_offset + count > (U32) mNumIndices) + if (indices_offset >= mNumIndices || + indices_offset + count > mNumIndices) { LL_ERRS() << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << LL_ENDL; } @@ -718,7 +716,7 @@ bool LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_of for (U32 i = start; i < end; i++) { U32 idx = (U32) (v[i][3]+0.25f); - if (idx >= shader->mFeatures.mIndexedTextureChannels) + if (idx >= (U32)shader->mFeatures.mIndexedTextureChannels) { LL_ERRS() << "Bad texture index found in vertex data stream." << LL_ENDL; } -- cgit v1.2.3 From 227e9be06832515fd10eb496d4a2a4528d1ebd92 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Mon, 10 Jun 2024 10:43:38 -0500 Subject: #1654 generate normals and tangents according to gltf specification (#1662) * Disable unloading of objects in background. * Add unlit GLTF shader variant --- indra/llrender/llglslshader.cpp | 71 +++++++++++---------------------------- indra/llrender/llglslshader.h | 25 ++++++-------- indra/llrender/llshadermgr.cpp | 3 +- indra/llrender/llshadermgr.h | 2 -- indra/llrender/llvertexbuffer.cpp | 16 +++++++++ indra/llrender/llvertexbuffer.h | 3 ++ 6 files changed, 49 insertions(+), 71 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 8ea134393a..6a60914b80 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -381,13 +381,10 @@ void LLGLSLShader::unloadInternal() stop_glerror(); } -bool LLGLSLShader::createShader(std::vector* attributes, - std::vector* uniforms, - U32 varying_count, - const char** varyings) +bool LLGLSLShader::createShader() { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; - + unloadInternal(); sInstances.insert(this); @@ -454,11 +451,11 @@ bool LLGLSLShader::createShader(std::vector* attributes, // Map attributes and uniforms if (success) { - success = mapAttributes(attributes); + success = mapAttributes(); } if (success) { - success = mapUniforms(uniforms); + success = mapUniforms(); } if (!success) { @@ -469,7 +466,7 @@ bool LLGLSLShader::createShader(std::vector* attributes, { 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); + return createShader(); } else { @@ -602,7 +599,7 @@ void LLGLSLShader::attachObjects(GLuint* objects, S32 count) } } -bool LLGLSLShader::mapAttributes(const std::vector* attributes) +bool LLGLSLShader::mapAttributes() { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; @@ -621,11 +618,10 @@ bool LLGLSLShader::mapAttributes(const std::vector* attrib } mAttribute.clear(); - U32 numAttributes = (attributes == NULL) ? 0 : attributes->size(); #if LL_RELEASE_WITH_DEBUG_INFO - mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, { -1, NULL }); + mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size(), { -1, NULL }); #else - mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, -1); + mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size(), -1); #endif if (res) @@ -649,19 +645,6 @@ bool LLGLSLShader::mapAttributes(const std::vector* attrib 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; } @@ -669,7 +652,7 @@ bool LLGLSLShader::mapAttributes(const std::vector* attrib return false; } -void LLGLSLShader::mapUniform(GLint index, const vector* uniforms) +void LLGLSLShader::mapUniform(GLint index) { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; @@ -756,21 +739,6 @@ void LLGLSLShader::mapUniform(GLint index, const vector* u 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; - } - } - } } } @@ -830,7 +798,7 @@ GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type, GLint return -1; } -bool LLGLSLShader::mapUniforms(const vector* uniforms) +bool LLGLSLShader::mapUniforms() { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; @@ -843,9 +811,8 @@ bool LLGLSLShader::mapUniforms(const vector* uniforms) 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); + mUniform.resize(LLShaderMgr::instance()->mReservedUniforms.size(), -1); + mTexture.resize(LLShaderMgr::instance()->mReservedUniforms.size(), -1); bind(); @@ -946,26 +913,26 @@ bool LLGLSLShader::mapUniforms(const vector* uniforms) if (specularDiff || bumpLessDiff || envLessDiff || refLessDiff) { - mapUniform(diffuseMap, uniforms); + mapUniform(diffuseMap); skip_index.insert(diffuseMap); if (-1 != specularMap) { - mapUniform(specularMap, uniforms); + mapUniform(specularMap); skip_index.insert(specularMap); } if (-1 != bumpMap) { - mapUniform(bumpMap, uniforms); + mapUniform(bumpMap); skip_index.insert(bumpMap); } if (-1 != environmentMap) { - mapUniform(environmentMap, uniforms); + mapUniform(environmentMap); skip_index.insert(environmentMap); } if (-1 != reflectionMap) { - mapUniform(reflectionMap, uniforms); + mapUniform(reflectionMap); skip_index.insert(reflectionMap); } } @@ -979,7 +946,7 @@ bool LLGLSLShader::mapUniforms(const vector* uniforms) if (skip_index.end() != skip_index.find(i)) continue; //........................................................................................ - mapUniform(i, uniforms); + mapUniform(i); } //........................................................................................................................................ @@ -1057,7 +1024,7 @@ void LLGLSLShader::bind() } } -void LLGLSLShader::bind(U32 variant) +void LLGLSLShader::bind(U8 variant) { llassert(mGLTFVariants.size() == LLGLSLShader::NUM_GLTF_VARIANTS); llassert(variant < LLGLSLShader::NUM_GLTF_VARIANTS); diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 8ebea2deca..f2b5c4881c 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -184,17 +184,14 @@ public: // 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* attributes, - std::vector* uniforms, - U32 varying_count = 0, - const char** varyings = NULL); + bool createShader(); 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* attributes); - bool mapUniforms(const std::vector*); - void mapUniform(GLint index, const std::vector*); + bool mapAttributes(); + bool mapUniforms(); + void mapUniform(GLint index); void uniform1i(U32 index, GLint i); void uniform1f(U32 index, GLfloat v); void fastUniform1f(U32 index, GLfloat v); @@ -330,22 +327,20 @@ public: // variants for use by GLTF renderer // bit 0 = alpha mode blend (1) or opaque (0) // bit 1 = rigged (1) or static (0) + // bit 2 = unlit (1) or lit (0) struct GLTFVariant { - constexpr static U32 RIGGED = 2; - constexpr static U32 ALPHA = 1; - constexpr static U32 OPAQUE_STATIC = 0; - constexpr static U32 ALPHA_STATIC = 1; - constexpr static U32 OPAQUE_RIGGED = 2; - constexpr static U32 ALPHA_RIGGED = 3; + constexpr static U8 ALPHA_BLEND = 1; + constexpr static U8 RIGGED = 2; + constexpr static U8 UNLIT = 4; }; - constexpr static U32 NUM_GLTF_VARIANTS = 4; + constexpr static U8 NUM_GLTF_VARIANTS = 8; std::vector mGLTFVariants; //helper to bind GLTF variant - void bind(U32 variant); + void bind(U8 variant); // hacky flag used for optimization in LLDrawPoolAlpha bool mCanBindFast = false; diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 4e8adb2fb3..4d53df5f06 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1157,6 +1157,7 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedAttribs.push_back("weight"); mReservedAttribs.push_back("weight4"); mReservedAttribs.push_back("clothing"); + mReservedAttribs.push_back("joint"); mReservedAttribs.push_back("texture_index"); //matrix state @@ -1239,7 +1240,6 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("heroProbes"); 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"); @@ -1363,7 +1363,6 @@ void LLShaderMgr::initAttribsAndUniforms() 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"); diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 03803c0e96..53e3d010db 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -107,7 +107,6 @@ public: HERO_PROBE, // "heroProbes" 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" @@ -217,7 +216,6 @@ public: DEFERRED_BLOOM, // "bloomMap" DEFERRED_PROJECTION, // "projectionMap" DEFERRED_NORM_MATRIX, // "norm_mat" - TEXTURE_GAMMA, // "texture_gamma" SPECULAR_COLOR, // "specular_color" ENVIRONMENT_INTENSITY, // "env_intensity" diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index fa3b2df6e0..a4d33c91df 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -600,6 +600,7 @@ const U32 LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_MAX] = sizeof(F32), // TYPE_WEIGHT, sizeof(LLVector4), // TYPE_WEIGHT4, sizeof(LLVector4), // TYPE_CLOTHWEIGHT, + sizeof(U64), // TYPE_JOINT, sizeof(LLVector4), // TYPE_TEXTURE_INDEX (actually exists as position.w), no extra data, but stride is 16 bytes }; @@ -617,6 +618,7 @@ static const std::string vb_type_name[] = "TYPE_WEIGHT", "TYPE_WEIGHT4", "TYPE_CLOTHWEIGHT", + "TYPE_JOINT" "TYPE_TEXTURE_INDEX", "TYPE_MAX", "TYPE_INDEX", @@ -1598,6 +1600,12 @@ void LLVertexBuffer::setupVertexBuffer() void* ptr = (void*)(base + mOffsets[TYPE_WEIGHT4]); glVertexAttribPointer(loc, 4, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_WEIGHT4], ptr); } + if (data_mask & MAP_JOINT) + { + AttributeType loc = TYPE_JOINT; + void* ptr = (void*)(base + mOffsets[TYPE_JOINT]); + glVertexAttribIPointer(loc, 4, GL_UNSIGNED_SHORT, LLVertexBuffer::sTypeSize[TYPE_JOINT], ptr); + } if (data_mask & MAP_CLOTHWEIGHT) { AttributeType loc = TYPE_CLOTHWEIGHT; @@ -1667,6 +1675,14 @@ void LLVertexBuffer::setWeight4Data(const LLVector4a* data) flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_WEIGHT4], mOffsets[TYPE_WEIGHT4] + sTypeSize[TYPE_WEIGHT4] * getNumVerts() - 1, (U8*) data, mMappedData); } +void LLVertexBuffer::setJointData(const U64* data) +{ +#if !LL_DARWIN + llassert(sGLRenderBuffer == mGLBuffer); +#endif + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_JOINT], mOffsets[TYPE_JOINT] + sTypeSize[TYPE_JOINT] * getNumVerts() - 1, (U8*) data, mMappedData); +} + void LLVertexBuffer::setIndexData(const U16* data) { #if !LL_DARWIN diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index f75849c82f..94339191a4 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -110,6 +110,7 @@ public: TYPE_WEIGHT, // "weight" TYPE_WEIGHT4, // "weight4" TYPE_CLOTHWEIGHT, // "clothing" + TYPE_JOINT, // "joint" 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 @@ -129,6 +130,7 @@ public: MAP_WEIGHT = (1< Date: Mon, 10 Jun 2024 18:16:13 +0200 Subject: Fix incorrect use of VX/VY/VZ/VW indices when color components are accessed --- indra/llrender/llfontgl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 7e10a4e159..59ee8ef84f 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -278,7 +278,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons 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]); + LLColor4U emoji_color(255, 255, 255, text_color.mV[VALPHA]); std::pair bitmap_entry = std::make_pair(EFontGlyphType::Grayscale, -1); S32 glyph_count = 0; -- cgit v1.2.3 From c95b4bf3ea2b681d6d05468b07e60fedb71fa2cf Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Mon, 10 Jun 2024 20:42:42 +0300 Subject: Post-merge - trim trailing whitespace --- indra/llrender/llrendertarget.cpp | 60 +++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index bd64ba84fb..1ffaec326b 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llrendertarget.cpp * @brief LLRenderTarget 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$ */ @@ -44,7 +44,7 @@ void check_framebuffer_status() break; default: LL_WARNS() << "check_framebuffer_status failed -- " << std::hex << status << LL_ENDL; - ll_fail("check_framebuffer_status failed"); + ll_fail("check_framebuffer_status failed"); break; } } @@ -75,10 +75,10 @@ LLRenderTarget::~LLRenderTarget() } void LLRenderTarget::resize(U32 resx, U32 resy) -{ +{ //for accounting, get the number of pixels added/subtracted S32 pix_diff = (resx*resy)-(mResX*mResY); - + mResX = resx; mResY = resy; @@ -92,7 +92,7 @@ void LLRenderTarget::resize(U32 resx, U32 resy) } if (mDepth) - { + { gGL.getTexUnit(0)->bindManual(mUsage, mDepth); U32 internal_type = LLTexUnit::getInternalType(mUsage); LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false); @@ -100,7 +100,7 @@ void LLRenderTarget::resize(U32 resx, U32 resy) sBytesAllocated += pix_diff*4; } } - + bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, LLTexUnit::eTextureType usage, LLTexUnit::eTextureMipGeneration generateMipMaps) { @@ -112,7 +112,7 @@ bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, LLT resy = llmin(resy, (U32) gGLManager.mGLMaxTextureSize); release(); - + mResX = resx; mResY = resy; @@ -125,7 +125,7 @@ bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, LLT // Calculate the number of mip levels based upon resolution that we should have. mMipLevels = 1 + floor(log10((float)llmax(mResX, mResY))/log10(2.0)); } - + if (depth) { if (!allocateDepth()) @@ -140,12 +140,12 @@ bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, LLT if (mDepth) { glBindFramebuffer(GL_FRAMEBUFFER, mFBO); - + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), mDepth, 0); glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); } - + return addColorAttachment(color_fmt); } @@ -190,7 +190,7 @@ void LLRenderTarget::releaseColorAttachment() llassert(!isBoundInStack()); llassert(mTex.size() == 1); //cannot use releaseColorAttachment with LLRenderTarget managed color targets llassert(mFBO != 0); // mFBO must be valid - + glBindFramebuffer(GL_FRAMEBUFFER, mFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, LLTexUnit::getInternalType(mUsage), 0, 0); glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); @@ -238,12 +238,12 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt) return false; } } - + sBytesAllocated += mResX*mResY*4; stop_glerror(); - + if (offset == 0) { //use bilinear filtering on single texture render targets that aren't multisampled gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); @@ -266,15 +266,15 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt) gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); stop_glerror(); } - + if (mFBO) { glBindFramebuffer(GL_FRAMEBUFFER, mFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+offset, LLTexUnit::getInternalType(mUsage), tex, 0); - + check_framebuffer_status(); - + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); } @@ -286,8 +286,8 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt) bindTarget(); flush(); } - - + + return true; } @@ -296,7 +296,7 @@ bool LLRenderTarget::allocateDepth() LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; LLImageGL::generateTextures(1, &mDepth); gGL.getTexUnit(0)->bindManual(mUsage, mDepth); - + U32 internal_type = LLTexUnit::getInternalType(mUsage); stop_glerror(); clear_glerror(); @@ -336,7 +336,7 @@ void LLRenderTarget::shareDepthBuffer(LLRenderTarget& target) if (mDepth) { glBindFramebuffer(GL_FRAMEBUFFER, target.mFBO); - + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), mDepth, 0); check_framebuffer_status(); @@ -355,7 +355,7 @@ void LLRenderTarget::release() if (mDepth) { LLImageGL::deleteTextures(1, &mDepth); - + mDepth = 0; sBytesAllocated -= mResX*mResY*4; @@ -408,7 +408,7 @@ void LLRenderTarget::release() mTex.clear(); mInternalFormat.clear(); - + mResX = mResY = 0; } @@ -417,7 +417,7 @@ void LLRenderTarget::bindTarget() LL_PROFILE_GPU_ZONE("bindTarget"); llassert(mFBO); llassert(!isBoundInStack()); - + glBindFramebuffer(GL_FRAMEBUFFER, mFBO); sCurFBO = mFBO; @@ -427,7 +427,7 @@ void LLRenderTarget::bindTarget() GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3}; glDrawBuffers(static_cast(mTex.size()), drawbuffers); - + if (mTex.empty()) { //no color buffer to draw to glDrawBuffer(GL_NONE); @@ -452,7 +452,7 @@ void LLRenderTarget::clear(U32 mask_in) if (mUseDepth) { mask |= GL_DEPTH_BUFFER_BIT; - + } if (mFBO) { @@ -560,7 +560,7 @@ bool LLRenderTarget::isBoundInStack() const { LLRenderTarget* cur = sBoundTarget; while (cur && cur != this) - { + { cur = cur->mPreviousRT; } -- cgit v1.2.3 From 429c92ad75fd3b3f7b9dfc52ed034b25004a3b9c Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Tue, 11 Jun 2024 13:27:54 -0500 Subject: #1687 Add support for KHR_texture_transform (#1717) --- indra/llrender/llshadermgr.cpp | 1 + indra/llrender/llshadermgr.h | 1 + 2 files changed, 2 insertions(+) (limited to 'indra/llrender') diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 4d53df5f06..5f30fc3879 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1178,6 +1178,7 @@ void LLShaderMgr::initAttribsAndUniforms() 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_occlusion_transform"); // (GLTF) mReservedUniforms.push_back("texture_emissive_transform"); // (GLTF) mReservedUniforms.push_back("terrain_texture_transforms"); // (GLTF) diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 53e3d010db..244fc41de6 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -56,6 +56,7 @@ public: 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_OCCLUSION_TRANSFORM, // "texture_occlusion_transform" (GLTF) TEXTURE_EMISSIVE_TRANSFORM, // "texture_emissive_transform" (GLTF) TERRAIN_TEXTURE_TRANSFORMS, // "terrain_texture_transforms" (GLTF) -- cgit v1.2.3 From f40fbdf4ad27a547e30781cd44cd6847d68d3300 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Tue, 11 Jun 2024 17:10:13 -0500 Subject: #1718 Add GLTF support for multiple texcoords (#1720) * Fix for GLTF MeshPrimitiveModes test --- indra/llrender/llglslshader.h | 4 +++- indra/llrender/llrender.cpp | 2 +- indra/llrender/llshadermgr.cpp | 5 +++++ indra/llrender/llshadermgr.h | 5 +++++ indra/llrender/llvertexbuffer.cpp | 10 +++++++++- indra/llrender/llvertexbuffer.h | 3 ++- 6 files changed, 25 insertions(+), 4 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index f2b5c4881c..d42df28809 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -328,14 +328,16 @@ public: // bit 0 = alpha mode blend (1) or opaque (0) // bit 1 = rigged (1) or static (0) // bit 2 = unlit (1) or lit (0) + // bit 3 = single (0) or multi (1) uv coordinates struct GLTFVariant { constexpr static U8 ALPHA_BLEND = 1; constexpr static U8 RIGGED = 2; constexpr static U8 UNLIT = 4; + constexpr static U8 MULTI_UV = 8; }; - constexpr static U8 NUM_GLTF_VARIANTS = 8; + constexpr static U8 NUM_GLTF_VARIANTS = 16; std::vector mGLTFVariants; diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index 51028e5667..cfefde3acc 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -1686,7 +1686,7 @@ void LLRender::flush() if (attribute_mask & LLVertexBuffer::MAP_TEXCOORD0) { - vb->setTexCoordData(mTexcoordsp.get()); + vb->setTexCoord0Data(mTexcoordsp.get()); } if (attribute_mask & LLVertexBuffer::MAP_COLOR) diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 5f30fc3879..6f4454f07a 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1180,6 +1180,11 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("texture_metallic_roughness_transform"); // (GLTF) mReservedUniforms.push_back("texture_occlusion_transform"); // (GLTF) mReservedUniforms.push_back("texture_emissive_transform"); // (GLTF) + mReservedUniforms.push_back("base_color_texcoord"); // (GLTF) + mReservedUniforms.push_back("emissive_texcoord"); // (GLTF) + mReservedUniforms.push_back("normal_texcoord"); // (GLTF) + mReservedUniforms.push_back("metallic_roughness_texcoord"); // (GLTF) + mReservedUniforms.push_back("occlusion_texcoord"); // (GLTF) mReservedUniforms.push_back("terrain_texture_transforms"); // (GLTF) diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 244fc41de6..c00aff3a9a 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -58,6 +58,11 @@ public: TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, // "texture_metallic_roughness_transform" (GLTF) TEXTURE_OCCLUSION_TRANSFORM, // "texture_occlusion_transform" (GLTF) TEXTURE_EMISSIVE_TRANSFORM, // "texture_emissive_transform" (GLTF) + BASE_COLOR_TEXCOORD, // "base_color_texcoord" (GLTF) + EMISSIVE_TEXCOORD, // "emissive_texcoord" (GLTF) + NORMAL_TEXCOORD, // "normal_texcoord" (GLTF) + METALLIC_ROUGHNESS_TEXCOORD, // "metallic_roughness_texcoord" (GLTF) + OCCLUSION_TEXCOORD, // "occlusion_texcoord" (GLTF) TERRAIN_TEXTURE_TRANSFORMS, // "terrain_texture_transforms" (GLTF) diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index a4d33c91df..33f7a6527f 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -1635,7 +1635,7 @@ void LLVertexBuffer::setPositionData(const LLVector4a* data) flush_vbo(GL_ARRAY_BUFFER, 0, sizeof(LLVector4a) * getNumVerts()-1, (U8*) data, mMappedData); } -void LLVertexBuffer::setTexCoordData(const LLVector2* data) +void LLVertexBuffer::setTexCoord0Data(const LLVector2* data) { #if !LL_DARWIN llassert(sGLRenderBuffer == mGLBuffer); @@ -1643,6 +1643,14 @@ void LLVertexBuffer::setTexCoordData(const LLVector2* data) flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD0], mOffsets[TYPE_TEXCOORD0] + sTypeSize[TYPE_TEXCOORD0] * getNumVerts() - 1, (U8*)data, mMappedData); } +void LLVertexBuffer::setTexCoord1Data(const LLVector2* data) +{ +#if !LL_DARWIN + llassert(sGLRenderBuffer == mGLBuffer); +#endif + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD1], mOffsets[TYPE_TEXCOORD1] + sTypeSize[TYPE_TEXCOORD1] * getNumVerts() - 1, (U8*)data, mMappedData); +} + void LLVertexBuffer::setColorData(const LLColor4U* data) { #if !LL_DARWIN diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index 94339191a4..66a7f2bf26 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -196,7 +196,8 @@ public: void setTangentData(const LLVector4a* data); void setWeight4Data(const LLVector4a* data); void setJointData(const U64* data); - void setTexCoordData(const LLVector2* data); + void setTexCoord0Data(const LLVector2* data); + void setTexCoord1Data(const LLVector2* data); void setColorData(const LLColor4U* data); void setIndexData(const U16* data); void setIndexData(const U32* data); -- cgit v1.2.3 From 8444cd9562a6a7b755fcb075864e205122354192 Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Wed, 12 Jun 2024 13:51:21 -0700 Subject: Fix whitespace pre-commit hook failures --- indra/llrender/llvertexbuffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index 66a7f2bf26..601096abf9 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -110,7 +110,7 @@ public: TYPE_WEIGHT, // "weight" TYPE_WEIGHT4, // "weight4" TYPE_CLOTHWEIGHT, // "clothing" - TYPE_JOINT, // "joint" + TYPE_JOINT, // "joint" 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 -- cgit v1.2.3