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 1ebf62e102cb3f328aa90809f7f5060b8f065db1 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 11 Jun 2024 15:40:09 +0200 Subject: Clean up line endings that got mangled up during last merge --- 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 491f5df6c9..efdde77a32 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 148786999dc1fb5cd409373cfedae485420a69d9 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 11 Jun 2024 18:35:24 +0300 Subject: viewer#1698 Uniform being out of range shouldn't crash viewer --- indra/llrender/llglslshader.cpp | 59 ++++++++++++++++++++++++++++------------- indra/llrender/llrender.h | 1 - 2 files changed, 40 insertions(+), 20 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 3697b4ca91..f5c698e20e 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -1094,7 +1094,8 @@ S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextu if (uniform < 0 || uniform >= (S32)mTexture.size()) { - LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << uniform << LL_ENDL; + llassert(false); return -1; } @@ -1115,6 +1116,8 @@ S32 LLGLSLShader::bindTexture(S32 uniform, LLRenderTarget* texture, bool depth, if (uniform < 0 || uniform >= (S32)mTexture.size()) { + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << uniform << LL_ENDL; + llassert(false); return -1; } @@ -1162,7 +1165,8 @@ S32 LLGLSLShader::unbindTexture(S32 uniform, LLTexUnit::eTextureType mode) if (uniform < 0 || uniform >= (S32)mTexture.size()) { - LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << uniform << LL_ENDL; + llassert(false); return -1; } @@ -1187,7 +1191,8 @@ S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTex if (uniform < 0 || uniform >= (S32)mTexture.size()) { - LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << uniform << LL_ENDL; + llassert(false); return -1; } @@ -1208,7 +1213,8 @@ S32 LLGLSLShader::disableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTe if (uniform < 0 || uniform >= (S32)mTexture.size()) { - LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << uniform << LL_ENDL; + llassert(false); return -1; } S32 index = mTexture[uniform]; @@ -1239,7 +1245,8 @@ void LLGLSLShader::uniform1i(U32 index, GLint x) { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1264,7 +1271,8 @@ void LLGLSLShader::uniform1f(U32 index, GLfloat x) { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1299,7 +1307,8 @@ void LLGLSLShader::uniform2f(U32 index, GLfloat x, GLfloat y) { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1325,7 +1334,8 @@ void LLGLSLShader::uniform3f(U32 index, GLfloat x, GLfloat y, GLfloat z) { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1351,7 +1361,8 @@ void LLGLSLShader::uniform4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1377,7 +1388,8 @@ void LLGLSLShader::uniform1iv(U32 index, U32 count, const GLint* v) { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1403,7 +1415,8 @@ void LLGLSLShader::uniform4iv(U32 index, U32 count, const GLint* v) { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1430,7 +1443,8 @@ void LLGLSLShader::uniform1fv(U32 index, U32 count, const GLfloat* v) { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1456,7 +1470,8 @@ void LLGLSLShader::uniform2fv(U32 index, U32 count, const GLfloat* v) { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1482,7 +1497,8 @@ void LLGLSLShader::uniform3fv(U32 index, U32 count, const GLfloat* v) { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1508,7 +1524,8 @@ void LLGLSLShader::uniform4fv(U32 index, U32 count, const GLfloat* v) { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1535,7 +1552,8 @@ void LLGLSLShader::uniformMatrix2fv(U32 index, U32 count, GLboolean transpose, c { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1555,7 +1573,8 @@ void LLGLSLShader::uniformMatrix3fv(U32 index, U32 count, GLboolean transpose, c { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1575,7 +1594,8 @@ void LLGLSLShader::uniformMatrix3x4fv(U32 index, U32 count, GLboolean transpose, { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } @@ -1595,7 +1615,8 @@ void LLGLSLShader::uniformMatrix4fv(U32 index, U32 count, GLboolean transpose, c { if (mUniform.size() <= index) { - LL_SHADER_UNIFORM_ERRS() << "Uniform index out of bounds." << LL_ENDL; + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); return; } diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h index ebdc9e751d..be9f3895e7 100644 --- a/indra/llrender/llrender.h +++ b/indra/llrender/llrender.h @@ -548,6 +548,5 @@ glh::matrix4f gl_perspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloa glh::matrix4f gl_lookat(LLVector3 eye, LLVector3 center, LLVector3 up); #define LL_SHADER_LOADING_WARNS(...) LL_WARNS() -#define LL_SHADER_UNIFORM_ERRS(...) LL_ERRS("Shader") #endif -- 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 From 15b3c9f1643368fe0c5356ea11288525bbe9fbb3 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Thu, 13 Jun 2024 02:22:03 +0200 Subject: Fix a few merge issues --- indra/llrender/llglslshader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index e47c842228..45abb695cb 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -279,7 +279,7 @@ bool LLGLSLShader::readProfileQuery(bool for_runtime, bool force_read) GLuint64 samples_passed = 0; glGetQueryObjectui64v(mSamplesQuery, GL_QUERY_RESULT, &samples_passed); - U64 primitives_generated = 0; + GLuint64 primitives_generated = 0; glGetQueryObjectui64v(mPrimitivesQuery, GL_QUERY_RESULT, &primitives_generated); sTotalTimeElapsed += time_elapsed; -- cgit v1.2.3 From 375555012f92c1b836f2d122754f9facd050be62 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 14 Jun 2024 17:06:07 -0500 Subject: 1736 audit texture image unit usage on os x (#1739) * #1736 Remove some unused samplers from glsl files and refactor shader manager to assume 16 texture image units and 4 indexed texture units all the time. --- indra/llrender/llglslshader.cpp | 47 ++++++++++++++++++++++++++++++----------- indra/llrender/llglslshader.h | 1 - indra/llrender/llshadermgr.cpp | 2 +- 3 files changed, 36 insertions(+), 14 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 45abb695cb..79979657f1 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -476,6 +476,7 @@ bool LLGLSLShader::createShader() } else if (mFeatures.mIndexedTextureChannels > 0) { //override texture channels for indexed texture rendering + llassert(mFeatures.mIndexedTextureChannels == LLGLSLShader::sIndexedTextureChannels); // these numbers must always match bind(); S32 channel_count = mFeatures.mIndexedTextureChannels; @@ -485,19 +486,41 @@ bool LLGLSLShader::createShader() uniform1i(uniName, i); } - S32 cur_tex = channel_count; //adjust any texture channels that might have been overwritten + //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) + if (mTexture[i] > -1) { - llassert(cur_tex < gGLManager.mNumTextureImageUnits); - uniform1i(i, cur_tex); - mTexture[i] = cur_tex++; + S32 new_tex = mTexture[i] + channel_count; + uniform1i(i, new_tex); + mTexture[i] = new_tex; } } + + // get the true number of active texture channels + mActiveTextureChannels = channel_count; + for (auto& tex : mTexture) + { + mActiveTextureChannels = llmax(mActiveTextureChannels, tex + 1); + } + + // when indexed texture channels are used, enforce an upper limit of 16 + // this should act as a canary in the coal mine for adding textures + // and breaking machines that are limited to 16 texture channels + llassert(mActiveTextureChannels <= 16); unbind(); } + LL_DEBUGS("GLSLTextureChannels") << mName << " has " << mActiveTextureChannels << " active texture channels" << LL_ENDL; + + for (U32 i = 0; i < mTexture.size(); i++) + { + if (mTexture[i] > -1) + { + LL_DEBUGS("GLSLTextureChannels") << "Texture " << LLShaderMgr::instance()->mReservedUniforms[i] << " assigned to channel " << mTexture[i] << LL_ENDL; + } + } + #ifdef LL_PROFILER_ENABLE_RENDER_DOC setLabel(mName.c_str()); #endif @@ -736,6 +759,10 @@ void LLGLSLShader::mapUniform(GLint index) //found it mUniform[i] = location; mTexture[i] = mapUniformTextureChannel(location, type, size); + if (mTexture[i] != -1) + { + LL_DEBUGS("GLSLTextureChannels") << name << " assigned to texture channel " << mTexture[i] << LL_ENDL; + } return; } } @@ -774,25 +801,21 @@ GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type, GLint 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); + GLint channel[16]; // <=== only support up to 16 texture channels + llassert(size <= 16); + size = llmin(size, 16); 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; diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 9339e7998d..cc2ba0fcff 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -52,7 +52,6 @@ public: bool hasSrgb = false; bool isDeferred = false; bool hasScreenSpaceReflections = false; - bool disableTextureIndex = false; bool hasAlphaMask = false; bool hasReflectionProbes = false; bool attachNothing = false; diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 634be615e6..574cf55a0e 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -295,7 +295,7 @@ bool LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) if (features->hasLighting) { - if (features->disableTextureIndex) + if (features->mIndexedTextureChannels <= 1) { if (features->hasAlphaMask) { -- cgit v1.2.3 From 80ea30af1a8b38360f71c29cc45872c4399dab0d Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 21 Jun 2024 13:13:08 -0500 Subject: #1769 gltf optimization pass (#1816) #1814 and #1517 Fix mirror update rate and occlusion culling --- indra/llrender/llglslshader.cpp | 6 ++- indra/llrender/llglslshader.h | 9 +++- indra/llrender/llrender.cpp | 2 + indra/llrender/llshadermgr.cpp | 2 + indra/llrender/llshadermgr.h | 2 + indra/llrender/llvertexbuffer.cpp | 106 +++++++++++++++++++++++++------------- indra/llrender/llvertexbuffer.h | 18 +++++++ 7 files changed, 105 insertions(+), 40 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index e22df46b28..25e4a88f28 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -54,6 +54,8 @@ using std::string; GLuint LLGLSLShader::sCurBoundShader = 0; LLGLSLShader* LLGLSLShader::sCurBoundShaderPtr = NULL; S32 LLGLSLShader::sIndexedTextureChannels = 0; +U32 LLGLSLShader::sMaxGLTFMaterials = 0; +U32 LLGLSLShader::sMaxGLTFNodes = 0; bool LLGLSLShader::sProfileEnabled = false; std::set LLGLSLShader::sInstances; LLGLSLShader::defines_map_t LLGLSLShader::sGlobalDefines; @@ -978,7 +980,9 @@ bool LLGLSLShader::mapUniforms() const char* ubo_names[] = { "ReflectionProbes", // UB_REFLECTION_PROBES - "GLTFJoints", // UB_GLTF_JOINTS + "GLTFJoints", // UB_GLTF_JOINTS + "GLTFNodes", // UB_GLTF_NODES + "GLTFMaterials", // UB_GLTF_MATERIALS }; llassert(LL_ARRAY_SIZE(ubo_names) == NUM_UNIFORM_BLOCKS); diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index cc2ba0fcff..86e5625dca 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -147,8 +147,10 @@ public: enum UniformBlock : GLuint { - UB_REFLECTION_PROBES, - UB_GLTF_JOINTS, + UB_REFLECTION_PROBES, // "ReflectionProbes" + UB_GLTF_JOINTS, // "GLTFJoints" + UB_GLTF_NODES, // "GLTFNodes" + UB_GLTF_MATERIALS, // "GLTFMaterials" NUM_UNIFORM_BLOCKS }; @@ -163,6 +165,9 @@ public: static LLGLSLShader* sCurBoundShaderPtr; static S32 sIndexedTextureChannels; + static U32 sMaxGLTFMaterials; + static U32 sMaxGLTFNodes; + static void initProfile(); static void finishProfile(bool emit_report = true); diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index cfefde3acc..a0209fab43 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -991,6 +991,8 @@ void LLRender::syncLightState() void LLRender::syncMatrices() { STOP_GLERROR; + LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; + static const U32 name[] = { LLShaderMgr::MODELVIEW_MATRIX, diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 574cf55a0e..a8e9f20b40 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1185,6 +1185,8 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("normal_texcoord"); // (GLTF) mReservedUniforms.push_back("metallic_roughness_texcoord"); // (GLTF) mReservedUniforms.push_back("occlusion_texcoord"); // (GLTF) + mReservedUniforms.push_back("gltf_node_id"); // (GLTF) + mReservedUniforms.push_back("gltf_material_id"); // (GLTF) mReservedUniforms.push_back("terrain_texture_transforms"); // (GLTF) diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index c00aff3a9a..fe6137c448 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -63,6 +63,8 @@ public: NORMAL_TEXCOORD, // "normal_texcoord" (GLTF) METALLIC_ROUGHNESS_TEXCOORD, // "metallic_roughness_texcoord" (GLTF) OCCLUSION_TEXCOORD, // "occlusion_texcoord" (GLTF) + GLTF_NODE_ID, // "gltf_node_id" (GLTF) + GLTF_MATERIAL_ID, // "gltf_material_id" (GLTF) TERRAIN_TEXTURE_TRANSFORMS, // "terrain_texture_transforms" (GLTF) diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index f82ec30242..2eb7c21f77 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -806,6 +806,13 @@ void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indi STOP_GLERROR; } +void LLVertexBuffer::drawRangeFast(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const +{ + glDrawRangeElements(sGLMode[mode], start, end, count, mIndicesType, + (GLvoid*)(indices_offset * (size_t)mIndicesStride)); +} + + void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const { drawRange(mode, 0, mNumVerts-1, count, indices_offset); @@ -1062,12 +1069,6 @@ bool LLVertexBuffer::updateNumVerts(U32 nverts) bool success = true; - if (nverts > 65536) - { - LL_WARNS() << "Vertex buffer overflow!" << LL_ENDL; - nverts = 65536; - } - U32 needed_size = calcOffsets(mTypeMask, mOffsets, nverts); if (needed_size != mSize) @@ -1210,7 +1211,7 @@ U8* LLVertexBuffer::mapIndexBuffer(U32 index, S32 count) // end -- last byte to copy (NOT last byte + 1) // data -- data to be flushed // dst -- mMappedData or mMappedIndexData -static void flush_vbo(GLenum target, U32 start, U32 end, void* data, U8* dst) +void LLVertexBuffer::flush_vbo(GLenum target, U32 start, U32 end, void* data, U8* dst) { #if LL_DARWIN LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb memcpy"); @@ -1218,6 +1219,8 @@ static void flush_vbo(GLenum target, U32 start, U32 end, void* data, U8* dst) // copy into mapped buffer memcpy(dst+start, data, end-start+1); #else + llassert(target == GL_ARRAY_BUFFER ? sGLRenderBuffer == mGLBuffer : sGLRenderIndices == mGLIndices); + // skip mapped data and stream to GPU via glBufferSubData if (end != 0) { @@ -1627,81 +1630,51 @@ void LLVertexBuffer::setupVertexBuffer() void LLVertexBuffer::setPositionData(const LLVector4a* data) { -#if !LL_DARWIN - llassert(sGLRenderBuffer == mGLBuffer); -#endif flush_vbo(GL_ARRAY_BUFFER, 0, sizeof(LLVector4a) * getNumVerts()-1, (U8*) data, mMappedData); } void LLVertexBuffer::setTexCoord0Data(const LLVector2* data) { -#if !LL_DARWIN - llassert(sGLRenderBuffer == mGLBuffer); -#endif 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 - llassert(sGLRenderBuffer == mGLBuffer); -#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); -#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); -#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); -#endif 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 - llassert(sGLRenderIndices == mGLIndices); -#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; @@ -1711,3 +1684,62 @@ void LLVertexBuffer::setIndexData(const U32* data) flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U32) * getNumIndices() - 1, (U8*)data, mMappedIndexData); } +void LLVertexBuffer::setPositionData(const LLVector4a* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, offset * sizeof(LLVector4a), (offset + count) * sizeof(LLVector4a) - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setNormalData(const LLVector4a* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_NORMAL] + offset * sTypeSize[TYPE_NORMAL], mOffsets[TYPE_NORMAL] + (offset + count) * sTypeSize[TYPE_NORMAL] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setTexCoord0Data(const LLVector2* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD0] + offset * sTypeSize[TYPE_TEXCOORD0], mOffsets[TYPE_TEXCOORD0] + (offset + count) * sTypeSize[TYPE_TEXCOORD0] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setTexCoord1Data(const LLVector2* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD1] + offset * sTypeSize[TYPE_TEXCOORD1], mOffsets[TYPE_TEXCOORD1] + (offset + count) * sTypeSize[TYPE_TEXCOORD1] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setColorData(const LLColor4U* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_COLOR] + offset * sTypeSize[TYPE_COLOR], mOffsets[TYPE_COLOR] + (offset + count) * sTypeSize[TYPE_COLOR] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setTangentData(const LLVector4a* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TANGENT] + offset * sTypeSize[TYPE_TANGENT], mOffsets[TYPE_TANGENT] + (offset + count) * sTypeSize[TYPE_TANGENT] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setWeight4Data(const LLVector4a* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_WEIGHT4] + offset * sTypeSize[TYPE_WEIGHT4], mOffsets[TYPE_WEIGHT4] + (offset + count) * sTypeSize[TYPE_WEIGHT4] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setJointData(const U64* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_JOINT] + offset * sTypeSize[TYPE_JOINT], mOffsets[TYPE_JOINT] + (offset + count) * sTypeSize[TYPE_JOINT] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setIndexData(const U16* data, U32 offset, U32 count) +{ + flush_vbo(GL_ELEMENT_ARRAY_BUFFER, offset * sizeof(U16), (offset + count) * sizeof(U16) - 1, (U8*)data, mMappedIndexData); +} + +void LLVertexBuffer::setIndexData(const U32* data, U32 offset, U32 count) +{ + 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, offset * sizeof(U32), (offset + count) * sizeof(U32) - 1, (U8*)data, mMappedIndexData); +} + + + + diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index 601096abf9..49500e28ce 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -202,6 +202,18 @@ public: void setIndexData(const U16* data); void setIndexData(const U32* data); + void setPositionData(const LLVector4a* data, U32 offset, U32 count); + void setNormalData(const LLVector4a* data, U32 offset, U32 count); + void setTangentData(const LLVector4a* data, U32 offset, U32 count); + void setWeight4Data(const LLVector4a* data, U32 offset, U32 count); + void setJointData(const U64* data, U32 offset, U32 count); + void setTexCoord0Data(const LLVector2* data, U32 offset, U32 count); + void setTexCoord1Data(const LLVector2* data, U32 offset, U32 count); + void setColorData(const LLColor4U* data, U32 offset, U32 count); + void setIndexData(const U16* data, U32 offset, U32 count); + void setIndexData(const U32* data, U32 offset, U32 count); + + U32 getNumVerts() const { return mNumVerts; } U32 getNumIndices() const { return mNumIndices; } @@ -219,6 +231,10 @@ public: void drawArrays(U32 mode, U32 offset, U32 count) const; void drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const; + // draw without syncing matrices. If you're positive there have been no matrix + // since the last call to syncMatrices, this is much faster than drawRange + void drawRangeFast(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; @@ -256,6 +272,8 @@ private: friend class LLNavShapeVBOManager; friend class LLNavMeshVBOManager; + void flush_vbo(GLenum target, U32 start, U32 end, void* data, U8* dst); + LLVertexBuffer(U32 typemask, U32 usage) : LLVertexBuffer(typemask) {} -- cgit v1.2.3 From ab87978cbc71cd4c83648627998055a010700f05 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Thu, 27 Jun 2024 13:12:43 -0500 Subject: 1836 dont store texture in system memory unless absolutely necessary (#1843) * #1836 Texture memory usage overhaul. Much decrufting - don't keep a copy of textures in system memory - use GPU to downrez textures instead of reloading from cache - use GPU to generate brightness/darkness bumpmaps --- indra/llrender/llgltexture.cpp | 21 ---- indra/llrender/llgltexture.h | 6 +- indra/llrender/llimagegl.cpp | 260 ++++++++++++++--------------------------- indra/llrender/llimagegl.h | 35 +++--- 4 files changed, 109 insertions(+), 213 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp index e614f45986..4dcca5a726 100644 --- a/indra/llrender/llgltexture.cpp +++ b/indra/llrender/llgltexture.cpp @@ -351,20 +351,6 @@ void LLGLTexture::forceUpdateBindStats(void) const 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()) ; @@ -372,13 +358,6 @@ bool LLGLTexture::isGLTextureCreated() const return mGLTexturep->isGLTextureCreated() ; } -S32 LLGLTexture::getDiscardLevelInAtlas() const -{ - llassert(mGLTexturep.notNull()) ; - - return mGLTexturep->getDiscardLevelInAtlas() ; -} - void LLGLTexture::destroyGLTexture() { if(mGLTexturep.notNull() && mGLTexturep->getHasGLTexture()) diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h index 0901243f8f..a7de20dc5c 100644 --- a/indra/llrender/llgltexture.h +++ b/indra/llrender/llgltexture.h @@ -51,10 +51,10 @@ public: BOOST_NONE = 0, BOOST_AVATAR , BOOST_AVATAR_BAKED , - BOOST_SCULPTED , BOOST_TERRAIN , // Needed for minimap generation for now. Lower than BOOST_HIGH so the texture stats don't get forced, i.e. texture stats are manually managed by minimap/terrain instead. BOOST_HIGH = 10, + BOOST_SCULPTED , BOOST_BUMP , BOOST_UNUSED_1 , // Placeholder to avoid disrupting habits around texture debug BOOST_SELECTED , @@ -75,7 +75,6 @@ public: AVATAR_SCRATCH_TEX, DYNAMIC_TEX, MEDIA, - ATLAS, OTHER, MAX_GL_IMAGE_CATEGORY }; @@ -156,10 +155,7 @@ public: bool isJustBound()const ; void forceUpdateBindStats(void) const; - U32 getTexelsInAtlas() const ; - U32 getTexelsInGLTexture() const ; bool isGLTextureCreated() const ; - S32 getDiscardLevelInAtlas() const ; LLGLTextureState getTextureState() const { return mTextureState; } //--------------------------------------------------------------------------------------------- diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 7e5cd628c1..956bcef352 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -41,6 +41,7 @@ #include "llrender.h" #include "llwindow.h" #include "llframetimer.h" +#include extern LL_COMMON_API bool on_main_thread(); @@ -130,10 +131,9 @@ 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; +std::unordered_set LLImageGL::sImageList; bool LLImageGLThread::sEnabledTextures = false; @@ -150,6 +150,9 @@ S32 LLImageGL::sMaxCategories = 1 ; //optimization for when we don't need to calculate mIsMask bool LLImageGL::sSkipAnalyzeAlpha; +U32 LLImageGL::sScratchPBO = 0; +U32 LLImageGL::sScratchPBOSize = 0; + //------------------------ //**************************************************************************************************** @@ -159,20 +162,6 @@ bool LLImageGL::sSkipAnalyzeAlpha; //************************************************************************************** //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 { @@ -252,6 +241,11 @@ void LLImageGL::initClass(LLWindow* window, S32 num_catagories, bool skip_analyz LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; sSkipAnalyzeAlpha = skip_analyze_alpha; + if (sScratchPBO == 0) + { + glGenBuffers(1, &sScratchPBO); + } + if (thread_texture_loads || thread_media_updates) { LLImageGLThread::createInstance(window); @@ -265,6 +259,12 @@ void LLImageGL::cleanupClass() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; LLImageGLThread::deleteSingleton(); + if (sScratchPBO != 0) + { + glDeleteBuffers(1, &sScratchPBO); + sScratchPBO = 0; + sScratchPBOSize = 0; + } } @@ -360,66 +360,19 @@ void LLImageGL::updateStats(F32 current_time) //---------------------------------------------------------------------------- //static -void LLImageGL::destroyGL(bool save_state) +void LLImageGL::destroyGL() { 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++) + for (auto& glimage : sImageList) { - LLImageGL* glimage = *iter; glimage->mTexOptionsDirty = true; stop_glerror(); } @@ -542,10 +495,6 @@ void LLImageGL::init(bool usemipmaps) mHeight = 0; mCurrentDiscardLevel = -1; - mDiscardLevelInAtlas = -1 ; - mTexelsInAtlas = 0 ; - mTexelsInGLTexture = 0 ; - mAllowCompression = true; mTarget = GL_TEXTURE_2D; @@ -622,9 +571,6 @@ bool LLImageGL::setSize(S32 width, S32 height, S32 ncomponents, S32 discard_leve return false; } - // pickmask validity depends on old image size, delete it - freePickMask(); - mWidth = width; mHeight = height; mComponents = ncomponents; @@ -1025,98 +971,6 @@ bool LLImageGL::setImage(const U8* data_in, bool data_hasmips /* = false */, S32 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; @@ -1752,7 +1606,6 @@ bool LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, bool data_ mTextureMemory = (S64Bytes)getMipBytes(mCurrentDiscardLevel); - mTexelsInGLTexture = getWidth() * getHeight(); // mark this as bound at this point, so we don't throw it out immediately mLastBindTime = sLastFrameTime; @@ -1830,8 +1683,7 @@ void LLImageGL::syncTexName(LLGLuint 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 ; + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; if (discard_level < 0) { @@ -2297,6 +2149,8 @@ void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h) //---------------------------------------------------------------------------- U32 LLImageGL::createPickMask(S32 pWidth, S32 pHeight) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + freePickMask(); U32 pick_width = pWidth/2 + 1; U32 pick_height = pHeight/2 + 1; @@ -2314,7 +2168,6 @@ U32 LLImageGL::createPickMask(S32 pWidth, S32 pHeight) //---------------------------------------------------------------------------- void LLImageGL::freePickMask() { - // pickmask validity depends on old image size, delete it if (mPickMask != NULL) { delete [] mPickMask; @@ -2352,16 +2205,16 @@ void LLImageGL::updatePickMask(S32 width, S32 height, const U8* data_in) return ; } - freePickMask(); - if (mFormatType != GL_UNSIGNED_BYTE || ((mFormatPrimary != GL_RGBA) && (mFormatPrimary != GL_SRGB_ALPHA))) { //cannot generate a pick mask for this texture + freePickMask(); return; } + #ifdef SHOW_ASSERT const U32 pickSize = createPickMask(width, height); #else // SHOW_ASSERT @@ -2460,6 +2313,73 @@ void LLImageGL::resetCurTexSizebar() sCurTexSizeBar = -1 ; sCurTexPickSize = -1 ; } + +bool LLImageGL::scaleDown(S32 desired_discard) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + + if (mTarget != GL_TEXTURE_2D) + { + return false; + } + + desired_discard = llmin(desired_discard, mMaxDiscardLevel); + + if (desired_discard <= mCurrentDiscardLevel) + { + return false; + } + + S32 mip = desired_discard - mCurrentDiscardLevel; + + S32 desired_width = getWidth(desired_discard); + S32 desired_height = getHeight(desired_discard); + + U64 size = getBytes(desired_discard); + llassert(size <= 2048*2048*4); // we shouldn't be using this method to downscale huge textures, but it'll work + gGL.getTexUnit(0)->bind(this); + + + if (sScratchPBO == 0) + { + glGenBuffers(1, &sScratchPBO); + sScratchPBOSize = 0; + } + + glBindBuffer(GL_PIXEL_PACK_BUFFER, sScratchPBO); + + if (size > sScratchPBOSize) + { + glBufferData(GL_PIXEL_PACK_BUFFER, size, NULL, GL_STREAM_COPY); + sScratchPBOSize = size; + } + + glGetTexImage(mTarget, mip, mFormatPrimary, mFormatType, nullptr); + + free_tex_image(mTexName); + + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, sScratchPBO); + glTexImage2D(mTarget, 0, mFormatPrimary, desired_width, desired_height, 0, mFormatPrimary, mFormatType, nullptr); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + alloc_tex_image(desired_width, desired_height, mFormatPrimary); + + if (mHasMipMaps) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glGenerateMipmap"); + glGenerateMipmap(mTarget); + } + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + mCurrentDiscardLevel = desired_discard; + + return true; +} + + //---------------------------------------------------------------------------- #if LL_IMAGEGL_THREAD_CHECK void LLImageGL::checkActiveThread() diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 5c7a5ce821..3892e9c014 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -39,6 +39,7 @@ #include "llrender.h" #include "threadpool.h" #include "workqueue.h" +#include #define LL_IMAGEGL_THREAD_CHECK 0 //set to 1 to enable thread debugging for ImageGL @@ -83,9 +84,8 @@ public: // 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(); + // cleanup GL state + static void destroyGL(); static void dirtyTexOptions(); static bool checkSize(S32 width, S32 height); @@ -148,6 +148,10 @@ public: S32 getDiscardLevel() const { return mCurrentDiscardLevel; } S32 getMaxDiscardLevel() const { return mMaxDiscardLevel; } + // override the current discard level + // should only be used for local textures where you know exactly what you're doing + void setDiscardLevel(S32 level) { mCurrentDiscardLevel = level; } + S32 getCurrentWidth() const { return mWidth ;} S32 getCurrentHeight() const { return mHeight ;} S32 getWidth(S32 discard_level = -1) const; @@ -194,26 +198,26 @@ public: 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;} - + LLGLenum getTexTarget()const { return mTarget; } 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 + // scale down to the desired discard level using GPU + // returns true if texture was scaled down + // desired discard will be clamped to max discard + // if desired discard is less than or equal to current discard, no scaling will occur + // only works for GL_TEXTURE_2D target + bool scaleDown(S32 desired_discard); + public: // Various GL/Rendering options S64Bytes mTextureMemory; @@ -240,15 +244,10 @@ private: 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: @@ -275,7 +274,7 @@ protected: // STATICS public: - static std::set sImageList; + static std::unordered_set sImageList; static S32 sCount; static F32 sLastFrameTime; @@ -301,6 +300,8 @@ public: private: static S32 sMaxCategories; static bool sSkipAnalyzeAlpha; + static U32 sScratchPBO; + static U32 sScratchPBOSize; //the flag to allow to call readBackRaw(...). //can be removed if we do not use that function at all. -- cgit v1.2.3 From 08b933a0c67463f06f124420f16c8a3f7dc83f1f Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 3 Jul 2024 17:42:24 -0500 Subject: #1870 Tune up for better experience on integrated intel with low memory (#1872) * More deterministic vsize calculation. Add control for choosing downscale method. * Quick hack to make GLTF preview work again --- indra/llrender/llgl.h | 3 + indra/llrender/llimagegl.cpp | 126 +++++++++++++++++++++++++++++--------- indra/llrender/llimagegl.h | 5 +- indra/llrender/llrender.h | 6 +- indra/llrender/llrendertarget.cpp | 12 +++- indra/llrender/llrendertarget.h | 4 +- 6 files changed, 120 insertions(+), 36 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 909dad2e85..be1c0a532a 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -102,6 +102,9 @@ public: bool mIsNVIDIA; bool mIsIntel; + // hints to the render pipe + U32 mDownScaleMethod = 0; // see settings.xml RenderDownScaleMethod + #if LL_DARWIN // Needed to distinguish problem cards on older Macs that break with Materials bool mIsMobileGF; diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 956bcef352..34200ef5cb 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -57,6 +57,9 @@ const F32 MIN_TEXTURE_LIFETIME = 10.f; U32 wpo2(U32 i); +U32 LLImageGL::sFrameCount = 0; + + // texture memory accounting (for macOS) static LLMutex sTexMemMutex; static std::unordered_map sTextureAllocs; @@ -1003,7 +1006,7 @@ bool should_stagger_image_set(bool compressed) #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(); + return !compressed && on_main_thread() && !gGLManager.mIsIntel; #endif } @@ -1185,13 +1188,37 @@ void LLImageGL::generateTextures(S32 numTextures, U32 *textures) } } +// static +void LLImageGL::updateClass() +{ + sFrameCount++; +} + // static void LLImageGL::deleteTextures(S32 numTextures, const U32 *textures) { + // wait a few frames before actually deleting the textures to avoid + // synchronization issues with the GPU + static std::vector sFreeList[4]; + if (gGLManager.mInited) { - free_tex_images(numTextures, textures); - glDeleteTextures(numTextures, textures); + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + U32 idx = sFrameCount % 4; + + for (S32 i = 0; i < numTextures; ++i) + { + sFreeList[idx].push_back(textures[i]); + } + + idx = (sFrameCount + 3) % 4; + + if (!sFreeList[idx].empty()) + { + glDeleteTextures((GLsizei) sFreeList[idx].size(), sFreeList[idx].data()); + free_tex_images((GLsizei) sFreeList[idx].size(), sFreeList[idx].data()); + sFreeList[idx].resize(0); + } } } @@ -2335,44 +2362,85 @@ bool LLImageGL::scaleDown(S32 desired_discard) S32 desired_width = getWidth(desired_discard); S32 desired_height = getHeight(desired_discard); - U64 size = getBytes(desired_discard); - llassert(size <= 2048*2048*4); // we shouldn't be using this method to downscale huge textures, but it'll work - gGL.getTexUnit(0)->bind(this); + if (gGLManager.mDownScaleMethod == 0) + { // use an FBO to downscale the texture + // allocate new texture + U32 temp_texname = 0; + generateTextures(1, &temp_texname); + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, temp_texname, true); + { + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glTexImage2D"); + glTexImage2D(mTarget, 0, mFormatPrimary, desired_width, desired_height, 0, mFormatPrimary, mFormatType, NULL); + } + // account for new texture getting created + alloc_tex_image(desired_width, desired_height, mFormatPrimary); - if (sScratchPBO == 0) - { - glGenBuffers(1, &sScratchPBO); - sScratchPBOSize = 0; - } + // Use render-to-texture to scale down the texture + { + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glFramebufferTexture2D"); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTarget, temp_texname, 0); + } - glBindBuffer(GL_PIXEL_PACK_BUFFER, sScratchPBO); + glViewport(0, 0, desired_width, desired_height); - if (size > sScratchPBOSize) - { - glBufferData(GL_PIXEL_PACK_BUFFER, size, NULL, GL_STREAM_COPY); - sScratchPBOSize = size; + // draw a full screen triangle + gGL.getTexUnit(0)->bind(this); + glDrawArrays(GL_TRIANGLES, 0, 3); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + // delete old texture and assign new texture name + deleteTextures(1, &mTexName); + mTexName = temp_texname; + + if (mHasMipMaps) + { // generate mipmaps if needed + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glGenerateMipmap"); + gGL.getTexUnit(0)->bind(this); + glGenerateMipmap(mTarget); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + } } + else + { // use a PBO to downscale the texture + U64 size = getBytes(desired_discard); + llassert(size <= 2048 * 2048 * 4); // we shouldn't be using this method to downscale huge textures, but it'll work + gGL.getTexUnit(0)->bind(this); + + if (sScratchPBO == 0) + { + glGenBuffers(1, &sScratchPBO); + sScratchPBOSize = 0; + } - glGetTexImage(mTarget, mip, mFormatPrimary, mFormatType, nullptr); + glBindBuffer(GL_PIXEL_PACK_BUFFER, sScratchPBO); - free_tex_image(mTexName); + if (size > sScratchPBOSize) + { + glBufferData(GL_PIXEL_PACK_BUFFER, size, NULL, GL_STREAM_COPY); + sScratchPBOSize = size; + } - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + glGetTexImage(mTarget, mip, mFormatPrimary, mFormatType, nullptr); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, sScratchPBO); - glTexImage2D(mTarget, 0, mFormatPrimary, desired_width, desired_height, 0, mFormatPrimary, mFormatType, nullptr); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + free_tex_image(mTexName); - alloc_tex_image(desired_width, desired_height, mFormatPrimary); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - if (mHasMipMaps) - { - LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glGenerateMipmap"); - glGenerateMipmap(mTarget); - } + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, sScratchPBO); + glTexImage2D(mTarget, 0, mFormatPrimary, desired_width, desired_height, 0, mFormatPrimary, mFormatType, nullptr); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + alloc_tex_image(desired_width, desired_height, mFormatPrimary); + + if (mHasMipMaps) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glGenerateMipmap"); + glGenerateMipmap(mTarget); + } + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + } mCurrentDiscardLevel = desired_discard; diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 3892e9c014..5073701c30 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -62,6 +62,9 @@ class LLImageGL : public LLRefCount friend class LLTexUnit; public: + // call once per frame + static void updateClass(); + // 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 @@ -276,7 +279,7 @@ protected: public: static std::unordered_set sImageList; static S32 sCount; - + static U32 sFrameCount; static F32 sLastFrameTime; // Global memory statistics diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h index be9f3895e7..e6b4118beb 100644 --- a/indra/llrender/llrender.h +++ b/indra/llrender/llrender.h @@ -297,7 +297,11 @@ public: ALTERNATE_DIFFUSE_MAP = 1, NORMAL_MAP = 1, SPECULAR_MAP = 2, - NUM_TEXTURE_CHANNELS = 3, + BASECOLOR_MAP = 3, + METALLIC_ROUGHNESS_MAP = 4, + GLTF_NORMAL_MAP = 5, + EMISSIVE_MAP = 6, + NUM_TEXTURE_CHANNELS = 7, }; enum eVolumeTexIndex : U8 diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index efdde77a32..60159a0497 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -426,14 +426,17 @@ void LLRenderTarget::bindTarget() GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3}; - glDrawBuffers(static_cast(mTex.size()), drawbuffers); if (mTex.empty()) { //no color buffer to draw to glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); } - + else + { + glDrawBuffers(static_cast(mTex.size()), drawbuffers); + glReadBuffer(GL_COLOR_ATTACHMENT0); + } check_framebuffer_status(); glViewport(0, 0, mResX, mResY); @@ -519,7 +522,8 @@ void LLRenderTarget::flush() llassert(sCurFBO == mFBO); llassert(sBoundTarget == this); - if (mGenerateMipMaps == LLTexUnit::TMG_AUTO) { + if (mGenerateMipMaps == LLTexUnit::TMG_AUTO) + { LL_PROFILE_GPU_ZONE("rt generate mipmaps"); bindTexture(0, 0, LLTexUnit::TFO_TRILINEAR); glGenerateMipmap(GL_TEXTURE_2D); @@ -540,6 +544,8 @@ void LLRenderTarget::flush() glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); sCurResX = gGLViewport[2]; sCurResY = gGLViewport[3]; + glReadBuffer(GL_BACK); + glDrawBuffer(GL_BACK); } } diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h index 340276a752..a1adf93fa1 100644 --- a/indra/llrender/llrendertarget.h +++ b/indra/llrender/llrendertarget.h @@ -172,6 +172,8 @@ public: // *HACK void swapFBORefs(LLRenderTarget& other); + static LLRenderTarget* sBoundTarget; + protected: U32 mResX; U32 mResY; @@ -186,8 +188,6 @@ protected: U32 mMipLevels; LLTexUnit::eTextureType mUsage; - - static LLRenderTarget* sBoundTarget; }; #endif -- cgit v1.2.3 From 9ddf64c65183960ffed4fe61c5d85e8bacaea030 Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Fri, 5 Jul 2024 18:46:30 +0200 Subject: #1931 BugSplat Crash: SecondLifeViewer!LLRenderTarget::release(387) --- indra/llrender/llrendertarget.cpp | 4 ++++ indra/llrender/llrendertarget.h | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 60159a0497..410efe9a70 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -50,6 +50,7 @@ void check_framebuffer_status() } } +bool LLRenderTarget::sInitFailed = false; bool LLRenderTarget::sUseFBO = false; U32 LLRenderTarget::sCurFBO = 0; @@ -352,6 +353,9 @@ void LLRenderTarget::release() LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; llassert(!isBoundInStack()); + if (sInitFailed) + return; + if (mDepth) { LLImageGL::deleteTextures(1, &mDepth); diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h index a1adf93fa1..fc78f059e0 100644 --- a/indra/llrender/llrendertarget.h +++ b/indra/llrender/llrendertarget.h @@ -61,7 +61,9 @@ class LLRenderTarget { public: - //whether or not to use FBO implementation + // Whether app initialization failed + static bool sInitFailed; + // Whether or not to use FBO implementation static bool sUseFBO; static U32 sBytesAllocated; static U32 sCurFBO; -- cgit v1.2.3 From d0173ac7fc1f999b31aad70c3abfa2c57f872899 Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Sat, 6 Jul 2024 09:23:39 +0200 Subject: #1931 Terminate AppViewer immediately in case of initialization failed --- indra/llrender/llrendertarget.cpp | 4 ---- indra/llrender/llrendertarget.h | 2 -- 2 files changed, 6 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 410efe9a70..60159a0497 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -50,7 +50,6 @@ void check_framebuffer_status() } } -bool LLRenderTarget::sInitFailed = false; bool LLRenderTarget::sUseFBO = false; U32 LLRenderTarget::sCurFBO = 0; @@ -353,9 +352,6 @@ void LLRenderTarget::release() LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; llassert(!isBoundInStack()); - if (sInitFailed) - return; - if (mDepth) { LLImageGL::deleteTextures(1, &mDepth); diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h index fc78f059e0..cd3290cf66 100644 --- a/indra/llrender/llrendertarget.h +++ b/indra/llrender/llrendertarget.h @@ -61,8 +61,6 @@ class LLRenderTarget { public: - // Whether app initialization failed - static bool sInitFailed; // Whether or not to use FBO implementation static bool sUseFBO; static U32 sBytesAllocated; -- cgit v1.2.3 From 9fdca96f8bd2211a99fe88e57b70cbecefa20b6d Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 8 Jul 2024 20:27:14 +0200 Subject: Re-enable compiler warnings C4244 and C4396 except for lltracerecording.h and llunittype.h for now --- indra/llrender/llfontfreetype.cpp | 2 +- indra/llrender/llfontfreetypesvg.cpp | 20 ++++++++++---------- indra/llrender/llfontgl.cpp | 4 ++-- indra/llrender/llfontregistry.cpp | 2 +- indra/llrender/llglslshader.cpp | 16 ++++++++-------- indra/llrender/llimagegl.cpp | 6 +++--- indra/llrender/llpostprocess.cpp | 2 +- indra/llrender/llrender2dutils.cpp | 14 +++++++------- indra/llrender/llrendertarget.cpp | 2 +- indra/llrender/llshadermgr.cpp | 8 ++++---- indra/llrender/lluiimage.cpp | 6 +++--- 11 files changed, 41 insertions(+), 41 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp index 741ed993b0..6128e03fa7 100644 --- a/indra/llrender/llfontfreetype.cpp +++ b/indra/llrender/llfontfreetype.cpp @@ -178,7 +178,7 @@ unsigned long ft_read_cb(FT_Stream stream, unsigned long offset, unsigned char * 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(); + return (unsigned long)file_stream->gcount(); } void ft_close_cb(FT_Stream stream) { diff --git a/indra/llrender/llfontfreetypesvg.cpp b/indra/llrender/llfontfreetypesvg.cpp index 355e8432aa..71f751329e 100644 --- a/indra/llrender/llfontfreetypesvg.cpp +++ b/indra/llrender/llfontfreetypesvg.cpp @@ -136,18 +136,18 @@ FT_Error LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, float svg_scale = llmin(svg_x_scale, svg_y_scale); datap->Scale = svg_scale; - glyph_slot->bitmap.width = floorf(svg_width) * svg_scale; - glyph_slot->bitmap.rows = floorf(svg_height) * svg_scale; + glyph_slot->bitmap.width = (unsigned int)(floorf(svg_width) * svg_scale); + glyph_slot->bitmap.rows = (unsigned int)(floorf(svg_height) * svg_scale); glyph_slot->bitmap_left = (document->metrics.x_ppem - glyph_slot->bitmap.width) / 2; - glyph_slot->bitmap_top = glyph_slot->face->size->metrics.ascender / 64.f; + glyph_slot->bitmap_top = (FT_Int)(glyph_slot->face->size->metrics.ascender / 64.f); glyph_slot->bitmap.pitch = glyph_slot->bitmap.width * 4; glyph_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; /* Copied as-is from fcft (MIT license) */ // Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box. - float horiBearingX = 0.; - float horiBearingY = -glyph_slot->bitmap_top; + float horiBearingX = 0.f; + float horiBearingY = -(float)glyph_slot->bitmap_top; // XXX parentheses correct? float vertBearingX = glyph_slot->metrics.horiBearingX / 64.0f - glyph_slot->metrics.horiAdvance / 64.0f / 2; @@ -156,13 +156,13 @@ FT_Error LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, // Do conversion in two steps to avoid 'bad function cast' warning glyph_slot->metrics.width = glyph_slot->bitmap.width * 64; glyph_slot->metrics.height = glyph_slot->bitmap.rows * 64; - glyph_slot->metrics.horiBearingX = horiBearingX * 64; - glyph_slot->metrics.horiBearingY = horiBearingY * 64; - glyph_slot->metrics.vertBearingX = vertBearingX * 64; - glyph_slot->metrics.vertBearingY = vertBearingY * 64; + glyph_slot->metrics.horiBearingX = (FT_Pos)(horiBearingX * 64); + glyph_slot->metrics.horiBearingY = (FT_Pos)(horiBearingY * 64); + glyph_slot->metrics.vertBearingX = (FT_Pos)(vertBearingX * 64); + glyph_slot->metrics.vertBearingY = (FT_Pos)(vertBearingY * 64); if (glyph_slot->metrics.vertAdvance == 0) { - glyph_slot->metrics.vertAdvance = glyph_slot->bitmap.rows * 1.2f * 64; + glyph_slot->metrics.vertAdvance = (FT_Pos)(glyph_slot->bitmap.rows * 1.2f * 64); } return FT_Err_Ok; diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 59ee8ef84f..04aebdde90 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -112,7 +112,7 @@ 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, bool use_color) const { - LLRectf rect_float(rect.mLeft, rect.mTop, rect.mRight, rect.mBottom); + LLRectf rect_float((F32)rect.mLeft, (F32)rect.mTop, (F32)rect.mRight, (F32)rect.mBottom); return render(wstr, begin_offset, rect_float, color, halign, valign, style, shadow, max_chars, right_x, use_ellipses, use_color); } @@ -138,7 +138,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRectf& rec 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); + return render(wstr, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, (S32)rect.getWidth(), right_x, use_ellipses, use_color); } diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index 62f4f35e3d..c48a389f6a 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -500,7 +500,7 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc) // *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 extra_scale = (is_fallback) ? fallback_scale : 1.0f; 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(); diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 25e4a88f28..e76a30a954 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -190,7 +190,7 @@ void LLGLSLShader::dumpStats() tris_sec /= seconds; F32 pct_samples = (F32)((F64)mSamplesDrawn / (F64)sTotalSamplesDrawn) * 100.f; - F32 samples_sec = (F32)mSamplesDrawn / 1000000000.0; + F32 samples_sec = (F32)(mSamplesDrawn / 1000000000.0); samples_sec /= seconds; F32 pct_binds = (F32)mBinds / (F32)sTotalBinds * 100.f; @@ -1265,7 +1265,7 @@ void LLGLSLShader::uniform1i(U32 index, GLint x) if (iter == mValue.end() || iter->second.mV[0] != x) { glUniform1i(mUniform[index], x); - mValue[mUniform[index]] = LLVector4(x, 0.f, 0.f, 0.f); + mValue[mUniform[index]] = LLVector4((F32)x, 0.f, 0.f, 0.f); } } } @@ -1405,7 +1405,7 @@ void LLGLSLShader::uniform1iv(U32 index, U32 count, const GLint* v) if (mUniform[index] >= 0) { const auto& iter = mValue.find(mUniform[index]); - LLVector4 vec(v[0], 0.f, 0.f, 0.f); + LLVector4 vec((F32)v[0], 0.f, 0.f, 0.f); if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1) { glUniform1iv(mUniform[index], count, v); @@ -1432,7 +1432,7 @@ void LLGLSLShader::uniform4iv(U32 index, U32 count, const GLint* v) if (mUniform[index] >= 0) { const auto& iter = mValue.find(mUniform[index]); - LLVector4 vec(v[0], v[1], v[2], v[3]); + LLVector4 vec((F32)v[0], (F32)v[1], (F32)v[2], (F32)v[3]); if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1) { glUniform1iv(mUniform[index], count, v); @@ -1702,7 +1702,7 @@ void LLGLSLShader::uniform1i(const LLStaticHashedString& uniform, GLint v) if (location >= 0) { const auto& iter = mValue.find(location); - LLVector4 vec(v, 0.f, 0.f, 0.f); + LLVector4 vec((F32)v, 0.f, 0.f, 0.f); if (iter == mValue.end() || shouldChange(iter->second, vec)) { glUniform1i(location, v); @@ -1718,7 +1718,7 @@ void LLGLSLShader::uniform1iv(const LLStaticHashedString& uniform, U32 count, co if (location >= 0) { - LLVector4 vec(v[0], 0, 0, 0); + LLVector4 vec((F32)v[0], 0.f, 0.f, 0.f); const auto& iter = mValue.find(location); if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1) { @@ -1736,7 +1736,7 @@ void LLGLSLShader::uniform4iv(const LLStaticHashedString& uniform, U32 count, co if (location >= 0) { - LLVector4 vec(v[0], v[1], v[2], v[3]); + LLVector4 vec((F32)v[0], (F32)v[1], (F32)v[2], (F32)v[3]); const auto& iter = mValue.find(location); if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1) { @@ -1755,7 +1755,7 @@ void LLGLSLShader::uniform2i(const LLStaticHashedString& uniform, GLint i, GLint if (location >= 0) { const auto& iter = mValue.find(location); - LLVector4 vec(i, j, 0.f, 0.f); + LLVector4 vec((F32)i, (F32)j, 0.f, 0.f); if (iter == mValue.end() || shouldChange(iter->second, vec)) { glUniform2i(location, i, j); diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 34200ef5cb..058afa0cf2 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -738,7 +738,7 @@ bool LLImageGL::setImage(const U8* data_in, bool data_hasmips /* = false */, S32 } if (is_compressed) { - S32 tex_size = dataFormatBytes(mFormatPrimary, w, h); + GLsizei tex_size = (GLsizei)dataFormatBytes(mFormatPrimary, w, h); glCompressedTexImage2D(mTarget, gl_level, mFormatPrimary, w, h, 0, tex_size, (GLvoid *)data_in); stop_glerror(); } @@ -941,7 +941,7 @@ bool LLImageGL::setImage(const U8* data_in, bool data_hasmips /* = false */, S32 S32 h = getHeight(); if (is_compressed) { - S32 tex_size = dataFormatBytes(mFormatPrimary, w, h); + GLsizei tex_size = (GLsizei)dataFormatBytes(mFormatPrimary, w, h); glCompressedTexImage2D(mTarget, 0, mFormatPrimary, w, h, 0, tex_size, (GLvoid *)data_in); stop_glerror(); } @@ -2418,7 +2418,7 @@ bool LLImageGL::scaleDown(S32 desired_discard) if (size > sScratchPBOSize) { glBufferData(GL_PIXEL_PACK_BUFFER, size, NULL, GL_STREAM_COPY); - sScratchPBOSize = size; + sScratchPBOSize = (U32)size; } glGetTexImage(mTarget, mip, mFormatPrimary, mFormatType, nullptr); diff --git a/indra/llrender/llpostprocess.cpp b/indra/llrender/llpostprocess.cpp index 8ebd09f20d..eef7193c92 100644 --- a/indra/llrender/llpostprocess.cpp +++ b/indra/llrender/llpostprocess.cpp @@ -343,7 +343,7 @@ 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.ortho( 0.f, (GLfloat) width , (GLfloat) height , 0.f, -1.f, 1.f ); gGL.matrixMode(LLRender::MM_MODELVIEW); gGL.pushMatrix(); gGL.loadIdentity(); diff --git a/indra/llrender/llrender2dutils.cpp b/indra/llrender/llrender2dutils.cpp index ef02fbd071..176c7a5d2c 100644 --- a/indra/llrender/llrender2dutils.cpp +++ b/indra/llrender/llrender2dutils.cpp @@ -364,7 +364,7 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTex { // 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); + const LLVector3 ui_translation = (gGL.getUITranslation() + LLVector3((F32)x, (F32)y, 0.f)).scaledVec(ui_scale); F32 uv_width = uv_outer_rect.getWidth(); F32 uv_height = uv_outer_rect.getHeight(); @@ -375,8 +375,8 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTex 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); + F32 image_width = (F32)image->getWidth(0); + F32 image_height = (F32)image->getHeight(0); S32 image_natural_width = ll_round(image_width * uv_width); S32 image_natural_height = ll_round(image_height * uv_height); @@ -413,10 +413,10 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTex 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]); + draw_center_rect.mLeft = (F32)ll_round(ui_translation.mV[VX] + (F32)draw_center_rect.mLeft * ui_scale.mV[VX]); + draw_center_rect.mTop = (F32)ll_round(ui_translation.mV[VY] + (F32)draw_center_rect.mTop * ui_scale.mV[VY]); + draw_center_rect.mRight = (F32)ll_round(ui_translation.mV[VX] + (F32)draw_center_rect.mRight * ui_scale.mV[VX]); + draw_center_rect.mBottom = (F32)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], diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 410efe9a70..015819c1bd 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -124,7 +124,7 @@ bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, LLT if (mGenerateMipMaps != LLTexUnit::TMG_NONE) { // Calculate the number of mip levels based upon resolution that we should have. - mMipLevels = 1 + floor(log10((float)llmax(mResX, mResY))/log10(2.0)); + mMipLevels = 1 + (U32)floor(log10((float)llmax(mResX, mResY)) / log10(2.0)); } if (depth) diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index a8e9f20b40..8bc00c0cf6 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1003,7 +1003,7 @@ void LLShaderMgr::initShaderCache(bool enabled, const LLUUID& old_cache_version, 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(); + binary_info.mLastUsedTime = (F32)data_pair.second["last_used"].asReal(); mShaderBinaryCache.insert_or_assign(LLUUID(data_pair.first), binary_info); } } @@ -1034,7 +1034,7 @@ void LLShaderMgr::persistShaderCacheMetadata() 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(); + const F32 current_time = (F32)LLTimer::getTotalSeconds(); for (auto it = mShaderBinaryCache.begin(); it != mShaderBinaryCache.end();) { const ProgramBinaryData& shader_metadata = it->second; @@ -1093,7 +1093,7 @@ bool LLShaderMgr::loadCachedProgramBinary(LLGLSLShader* shader) glGetProgramiv(shader->mProgramObject, GL_LINK_STATUS, &success); if (error == GL_NO_ERROR && success == GL_TRUE) { - binary_iter->second.mLastUsedTime = LLTimer::getTotalSeconds(); + binary_iter->second.mLastUsedTime = (F32)LLTimer::getTotalSeconds(); LL_INFOS() << "Loaded cached binary for shader: " << shader->mName << LL_ENDL; return true; } @@ -1131,7 +1131,7 @@ bool LLShaderMgr::saveCachedProgramBinary(LLGLSLShader* shader) fwrite(program_binary.data(), sizeof(U8), program_binary.size(), outfile); outfile.close(); - binary_info.mLastUsedTime = LLTimer::getTotalSeconds(); + binary_info.mLastUsedTime = (F32)LLTimer::getTotalSeconds(); mShaderBinaryCache.insert_or_assign(shader->mShaderHash, binary_info); return true; diff --git a/indra/llrender/lluiimage.cpp b/indra/llrender/lluiimage.cpp index bcf665ca18..d31a91e2af 100644 --- a/indra/llrender/lluiimage.cpp +++ b/indra/llrender/lluiimage.cpp @@ -83,7 +83,7 @@ void LLUIImage::draw3D(const LLVector3& origin_agent, const LLVector3& x_axis, c LLRender2D::getInstance()->pushMatrix(); { - LLVector3 rect_origin = origin_agent + (rect.mLeft * x_axis) + (rect.mBottom * y_axis); + LLVector3 rect_origin = origin_agent + ((F32)rect.mLeft * x_axis) + ((F32)rect.mBottom * y_axis); LLRender2D::getInstance()->translate(rect_origin.mV[VX], rect_origin.mV[VY], rect_origin.mV[VZ]); @@ -100,8 +100,8 @@ void LLUIImage::draw3D(const LLVector3& origin_agent, const LLVector3& x_axis, c (rect.getHeight() - (border_height * border_scale * 0.5f)) / (F32)rect.getHeight(), (rect.getWidth() - (border_width * border_scale * 0.5f)) / (F32)rect.getWidth(), (border_height * border_scale * 0.5f) / (F32)rect.getHeight()), - rect.getWidth() * x_axis, - rect.getHeight() * y_axis); + (F32)rect.getWidth() * x_axis, + (F32)rect.getHeight() * y_axis); } LLRender2D::getInstance()->popMatrix(); } -- cgit v1.2.3 From 2a7030992faa12c362d3eb9365080efd8265e15f Mon Sep 17 00:00:00 2001 From: Rye Mutt Date: Tue, 9 Jul 2024 17:53:43 -0400 Subject: Update tracy profiler to 0.10 (#1946) --- indra/llrender/llfontgl.cpp | 8 ++++---- indra/llrender/llrender.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 04aebdde90..b9a4235a4e 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -560,7 +560,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars void LLFontGL::generateASCIIglyphs() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; for (U32 i = 32; (i < 127); i++) { mFontFreetype->getGlyphInfo(i, EFontGlyphType::Grayscale); @@ -570,7 +570,7 @@ void LLFontGL::generateASCIIglyphs() // 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 + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; if (!wchars || !wchars[0] || max_chars == 0) { return 0; @@ -881,7 +881,7 @@ void LLFontGL::dumpFontTextures() // static bool LLFontGL::loadDefaultFonts() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; bool succ = true; succ &= (NULL != getFontSansSerifSmall()); succ &= (NULL != getFontSansSerif()); @@ -894,7 +894,7 @@ bool LLFontGL::loadDefaultFonts() void LLFontGL::loadCommonFonts() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; getFont(LLFontDescriptor("SansSerif", "Small", BOLD)); getFont(LLFontDescriptor("SansSerif", "Large", BOLD)); getFont(LLFontDescriptor("SansSerif", "Huge", BOLD)); diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index a0209fab43..1301d325a0 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -1514,7 +1514,7 @@ LLLightState* LLRender::getLight(U32 index) void LLRender::setAmbientLightColor(const LLColor4& color) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE + LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE; if (color != mAmbientLightColor) { ++mLightHash; -- cgit v1.2.3 From e101d1aa28a51570e6d76dc12df810b1daa71e7e Mon Sep 17 00:00:00 2001 From: Rye Mutt Date: Tue, 9 Jul 2024 20:51:02 -0400 Subject: Fix excessive clearing of shader cache when toggling mirrors (#1950) --- indra/llrender/llshadermgr.cpp | 6 ------ indra/llrender/llshadermgr.h | 1 - 2 files changed, 7 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 8bc00c0cf6..bc542d325e 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -44,7 +44,6 @@ using std::make_pair; using std::string; LLShaderMgr * LLShaderMgr::sInstance = NULL; -bool LLShaderMgr::sMirrorsEnabled = false; LLShaderMgr::LLShaderMgr() { @@ -600,11 +599,6 @@ GLuint LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_lev extra_code_text[extra_code_count++] = strdup("#define FXAA_GLSL_130 1\n"); } - if (sMirrorsEnabled) - { - extra_code_text[extra_code_count++] = strdup("#define HERO_PROBES 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 diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index fe6137c448..921ef20997 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -378,7 +378,6 @@ public: bool mShaderCacheInitialized = false; bool mShaderCacheEnabled = false; std::string mShaderCacheDir; - static bool sMirrorsEnabled; protected: -- cgit v1.2.3 From 604cb4cb4dd71c0f90633e50d5b0108e3901c4ad Mon Sep 17 00:00:00 2001 From: Rye Mutt Date: Fri, 26 Jul 2024 06:19:34 -0400 Subject: Reduce utf8 to wstring conversion and llwstring temporaries during text draw (#2115) --- indra/llrender/llfontgl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index b9a4235a4e..b6cdb81b33 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -406,7 +406,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons // recursively render ellipses at end of string // we've already reserved enough room gGL.pushUIMatrix(); - renderUTF8(std::string("..."), + static LLWString elipses_wstr(utf8string_to_wstring(std::string("..."))); + render(elipses_wstr, 0, (cur_x - origin.mV[VX]) / sScaleX, (F32)y, color, -- cgit v1.2.3 From 75455d101c9535d0d45aa0f505f888f4ba3de64d Mon Sep 17 00:00:00 2001 From: Rye Mutt Date: Tue, 6 Aug 2024 09:21:19 -0400 Subject: Convert LLUI and LLRender2D to LLSimpleton to reduce overhead during ui draw (#2202) --- indra/llrender/llrender2dutils.cpp | 2 +- indra/llrender/llrender2dutils.h | 7 ++++--- indra/llrender/lluiimage.cpp | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llrender2dutils.cpp b/indra/llrender/llrender2dutils.cpp index 176c7a5d2c..428370057e 100644 --- a/indra/llrender/llrender2dutils.cpp +++ b/indra/llrender/llrender2dutils.cpp @@ -726,7 +726,7 @@ void gl_line_3d( const LLVector3& start, const LLVector3& end, const LLColor4& c } gGL.end(); - LLRender2D::getInstance()->setLineWidth(1.f); + LLRender2D::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) diff --git a/indra/llrender/llrender2dutils.h b/indra/llrender/llrender2dutils.h index 0d3efc38d6..096e7584f1 100644 --- a/indra/llrender/llrender2dutils.h +++ b/indra/llrender/llrender2dutils.h @@ -122,12 +122,13 @@ inline void gl_rect_2d_offset_local( const LLRect& rect, S32 pixel_offset, bool class LLImageProviderInterface; -class LLRender2D : public LLParamSingleton +class LLRender2D : public LLSimpleton { - LLSINGLETON(LLRender2D, LLImageProviderInterface* image_provider); LOG_CLASS(LLRender2D); - ~LLRender2D(); public: + LLRender2D(LLImageProviderInterface* image_provider); + ~LLRender2D(); + static void pushMatrix(); static void popMatrix(); static void loadIdentity(); diff --git a/indra/llrender/lluiimage.cpp b/indra/llrender/lluiimage.cpp index d31a91e2af..dc18bf16bf 100644 --- a/indra/llrender/lluiimage.cpp +++ b/indra/llrender/lluiimage.cpp @@ -81,10 +81,10 @@ void LLUIImage::draw3D(const LLVector3& origin_agent, const LLVector3& x_axis, c } } - LLRender2D::getInstance()->pushMatrix(); + LLRender2D::pushMatrix(); { LLVector3 rect_origin = origin_agent + ((F32)rect.mLeft * x_axis) + ((F32)rect.mBottom * y_axis); - LLRender2D::getInstance()->translate(rect_origin.mV[VX], + LLRender2D::translate(rect_origin.mV[VX], rect_origin.mV[VY], rect_origin.mV[VZ]); gGL.getTexUnit(0)->bind(getImage()); @@ -103,7 +103,7 @@ void LLUIImage::draw3D(const LLVector3& origin_agent, const LLVector3& x_axis, c (F32)rect.getWidth() * x_axis, (F32)rect.getHeight() * y_axis); - } LLRender2D::getInstance()->popMatrix(); + } LLRender2D::popMatrix(); } //#include "lluiimage.inl" -- cgit v1.2.3 From 6aa9110b2ae346da1a396500cae1b41e8e2d5b75 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Thu, 11 Jul 2024 15:54:24 -0700 Subject: secondlife/viewer#1883: Local-only PBR terrain paintmap with developer tools --- indra/llrender/llshadermgr.cpp | 3 +++ indra/llrender/llshadermgr.h | 3 +++ 2 files changed, 6 insertions(+) (limited to 'indra/llrender') diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index bc542d325e..512ef340f9 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1409,6 +1409,7 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("detail_3"); mReservedUniforms.push_back("alpha_ramp"); + mReservedUniforms.push_back("paint_map"); mReservedUniforms.push_back("detail_0_base_color"); mReservedUniforms.push_back("detail_1_base_color"); @@ -1433,6 +1434,8 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("emissiveColors"); mReservedUniforms.push_back("minimum_alphas"); + mReservedUniforms.push_back("region_scale"); + mReservedUniforms.push_back("origin"); mReservedUniforms.push_back("display_gamma"); diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 921ef20997..2b76ef664b 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -267,6 +267,7 @@ public: TERRAIN_DETAIL3, // "detail_3" TERRAIN_ALPHARAMP, // "alpha_ramp" + TERRAIN_PAINTMAP, // "paint_map" TERRAIN_DETAIL0_BASE_COLOR, // "detail_0_base_color" (GLTF) TERRAIN_DETAIL1_BASE_COLOR, // "detail_1_base_color" (GLTF) @@ -291,6 +292,8 @@ public: TERRAIN_EMISSIVE_COLORS, // "emissiveColors" (GLTF) TERRAIN_MINIMUM_ALPHAS, // "minimum_alphas" (GLTF) + REGION_SCALE, // "region_scale" (GLTF) + SHINY_ORIGIN, // "origin" DISPLAY_GAMMA, // "display_gamma" -- cgit v1.2.3 From 17f7b439d617bfdf4c865f1478ade662e86b61d2 Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Wed, 7 Aug 2024 20:56:10 +0200 Subject: #1922 Make PBR scale and offset crosshair work like Texture crosshair do --- indra/llrender/llrender.h | 19 +++++++++++-------- indra/llrender/llvertexbuffer.cpp | 12 ++++++++++++ indra/llrender/llvertexbuffer.h | 9 +++++---- 3 files changed, 28 insertions(+), 12 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h index e6b4118beb..010ab122b6 100644 --- a/indra/llrender/llrender.h +++ b/indra/llrender/llrender.h @@ -293,15 +293,18 @@ public: enum eTexIndex : U8 { - DIFFUSE_MAP = 0, - ALTERNATE_DIFFUSE_MAP = 1, - NORMAL_MAP = 1, - SPECULAR_MAP = 2, - BASECOLOR_MAP = 3, + // Channels for material textures + DIFFUSE_MAP = 0, + ALTERNATE_DIFFUSE_MAP = 1, + NORMAL_MAP = 1, + SPECULAR_MAP = 2, + // Channels for PBR textures + BASECOLOR_MAP = 3, METALLIC_ROUGHNESS_MAP = 4, - GLTF_NORMAL_MAP = 5, - EMISSIVE_MAP = 6, - NUM_TEXTURE_CHANNELS = 7, + GLTF_NORMAL_MAP = 5, + EMISSIVE_MAP = 6, + // Total number of channels + NUM_TEXTURE_CHANNELS = 7, }; enum eVolumeTexIndex : U8 diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 2eb7c21f77..02afcf12c6 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -794,6 +794,18 @@ void LLVertexBuffer::setLabel(const char* label) { } #endif +void LLVertexBuffer::clone(LLVertexBuffer& target) const +{ + target.mTypeMask = mTypeMask; + target.mIndicesType = mIndicesType; + target.mIndicesStride = mIndicesStride; + if (target.getNumVerts() != getNumVerts() || + target.getNumIndices() != getNumIndices()) + { + target.allocateBuffer(getNumVerts(), getNumIndices()); + } +} + void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const { llassert(validateRange(start, end, count, indices_offset)); diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index 49500e28ce..4ada0c335b 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -163,13 +163,13 @@ public: // set for rendering // assumes (and will assert on) the following: - // - this buffer has no pending unampBuffer call + // - this buffer has no pending unmapBuffer call // - a shader is currently bound // - This buffer has sufficient attributes within it to satisfy the needs of the currently bound shader void setBuffer(); // Only call each getVertexPointer, etc, once before calling unmapBuffer() - // call unmapBuffer() after calls to getXXXStrider() before any cals to setBuffer() + // call unmapBuffer() after calls to getXXXStrider() before any calls to setBuffer() // example: // vb->getVertexBuffer(verts); // vb->getNormalStrider(norms); @@ -218,12 +218,12 @@ public: U32 getNumIndices() const { return mNumIndices; } U32 getTypeMask() const { return mTypeMask; } - bool hasDataType(AttributeType type) const { return ((1 << type) & getTypeMask()); } + 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]; } + 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 @@ -242,6 +242,7 @@ public: void setLabel(const char* label); #endif + void clone(LLVertexBuffer& target) const; protected: U32 mGLBuffer = 0; // GL VBO handle -- cgit v1.2.3 From ed6732eda8c80d17e05bbc6563b6ce08ef6b8c72 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Tue, 20 Aug 2024 06:59:07 -0500 Subject: #2315 Ensure textures are deleted when they are no longer referenced. (#2343) * #2315 WIP -- Clean up deletion rules in texture list. Incidental decruft. * Touch up assertions. * Move mLastReferencedTimer to LLViewerMediaTexture since it's no longer used by LLViewerTexture --- indra/llrender/llgltexture.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h index a7de20dc5c..122d2a7f9c 100644 --- a/indra/llrender/llgltexture.h +++ b/indra/llrender/llgltexture.h @@ -82,8 +82,6 @@ public: 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; -- cgit v1.2.3 From db84bf9567c27e266ccc6b1b6aed089c6022fe91 Mon Sep 17 00:00:00 2001 From: Rye Mutt Date: Wed, 21 Aug 2024 10:47:31 -0400 Subject: Improve accuracy of texture memory tracking (#2371) * Fix alloc_tex_image to account for more missing texture memory Change alloc_tex_image calls to pass internal format to properly account for used image type * Fix scaleDown passing primary format in place of internal format to glTexImage2D * Make texture debug view and texture bias calculation consistent and remove double accounting for render target textures --- indra/llrender/llcubemaparray.cpp | 14 ++++++-------- indra/llrender/llimagegl.cpp | 19 ++++++++++++------- indra/llrender/llimagegl.h | 2 +- 3 files changed, 19 insertions(+), 16 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llcubemaparray.cpp b/indra/llrender/llcubemaparray.cpp index be69b997da..4f5e13765a 100644 --- a/indra/llrender/llcubemaparray.cpp +++ b/indra/llrender/llcubemaparray.cpp @@ -125,27 +125,25 @@ void LLCubeMapArray::allocate(U32 resolution, U32 components, U32 count, bool us mImage->setHasMipMaps(use_mips); bind(0); + free_cur_tex_image(); U32 format = components == 4 ? GL_RGBA16F : GL_RGB16F; - U32 mip = 0; - - free_cur_tex_image(); - - while (resolution >= 1) + U32 mip_resolution = resolution; + while (mip_resolution >= 1) { - glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, format, resolution, resolution, count * 6, 0, + glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, format, mip_resolution, mip_resolution, count * 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); if (!use_mips) { break; } - resolution /= 2; + mip_resolution /= 2; ++mip; } - alloc_tex_image(resolution * 6, resolution, format); + alloc_tex_image(resolution, resolution, format, count * 6); mImage->setAddressMode(LLTexUnit::TAM_CLAMP); diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 058afa0cf2..0746e21079 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -67,11 +67,12 @@ 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) +void LLImageGLMemory::alloc_tex_image(U32 width, U32 height, U32 intformat, U32 count) { U32 texUnit = gGL.getCurrentTexUnitIndex(); U32 texName = gGL.getTexUnit(texUnit)->getCurrTexture(); - U64 size = LLImageGL::dataFormatBytes(pixformat, width, height); + U64 size = LLImageGL::dataFormatBytes(intformat, width, height); + size *= count; llassert(size >= 0); @@ -291,9 +292,13 @@ S32 LLImageGL::dataFormatBits(S32 dataformat) case GL_SRGB: return 24; case GL_RGB8: return 24; case GL_RGBA: return 32; + case GL_RGBA8: 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_DEPTH_COMPONENT24: return 24; + case GL_R16F: return 16; + case GL_RG16F: return 32; case GL_RGB16F: return 48; case GL_RGBA16F: return 64; default: @@ -1384,7 +1389,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt sub_image_lines(target, miplevel, 0, 0, width, height, pixformat, pixtype, src, width); } } - alloc_tex_image(width, height, pixformat); + alloc_tex_image(width, height, intformat, 1); } stop_glerror(); @@ -2370,11 +2375,11 @@ bool LLImageGL::scaleDown(S32 desired_discard) gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, temp_texname, true); { LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glTexImage2D"); - glTexImage2D(mTarget, 0, mFormatPrimary, desired_width, desired_height, 0, mFormatPrimary, mFormatType, NULL); + glTexImage2D(mTarget, 0, mFormatInternal, desired_width, desired_height, 0, mFormatPrimary, mFormatType, NULL); } // account for new texture getting created - alloc_tex_image(desired_width, desired_height, mFormatPrimary); + alloc_tex_image(desired_width, desired_height, mFormatInternal, 1); // Use render-to-texture to scale down the texture { @@ -2428,10 +2433,10 @@ bool LLImageGL::scaleDown(S32 desired_discard) glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, sScratchPBO); - glTexImage2D(mTarget, 0, mFormatPrimary, desired_width, desired_height, 0, mFormatPrimary, mFormatType, nullptr); + glTexImage2D(mTarget, 0, mFormatInternal, desired_width, desired_height, 0, mFormatPrimary, mFormatType, nullptr); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - alloc_tex_image(desired_width, desired_height, mFormatPrimary); + alloc_tex_image(desired_width, desired_height, mFormatInternal, 1); if (mHasMipMaps) { diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 5073701c30..9a8d935b89 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -50,7 +50,7 @@ class LLWindow; namespace LLImageGLMemory { - void alloc_tex_image(U32 width, U32 height, U32 pixformat); + void alloc_tex_image(U32 width, U32 height, U32 intformat, U32 count); void free_tex_image(U32 texName); void free_tex_images(U32 count, const U32* texNames); void free_cur_tex_image(); -- cgit v1.2.3 From 0e59ed204fdf84c5323837e5c91cfa7da0a9e89b Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 21 Aug 2024 14:36:18 -0500 Subject: Fix crash on Intel GPUs (or anywhere OpenGL compatibility profile is used). (#2387) --- indra/llrender/llimagegl.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indra/llrender') diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 0746e21079..be30807e7f 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -284,10 +284,13 @@ S32 LLImageGL::dataFormatBits(S32 dataformat) 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_LUMINANCE8: return 8; case GL_ALPHA: return 8; + case GL_ALPHA8: return 8; case GL_RED: return 8; case GL_COLOR_INDEX: return 8; case GL_LUMINANCE_ALPHA: return 16; + case GL_LUMINANCE8_ALPHA8: return 16; case GL_RGB: return 24; case GL_SRGB: return 24; case GL_RGB8: return 24; -- cgit v1.2.3 From 28331b2385a8de2a0fde67329127c1694e3f219f Mon Sep 17 00:00:00 2001 From: Rye Mutt Date: Thu, 22 Aug 2024 11:25:25 -0400 Subject: Do deprecated texture swizzle on GPU using GL texture swizzling functions (#2389) * Use GL texture swizzling instead of scratch buffer for deprecated formats when GL 3.3 is available Fix crash when GL texture compression is enabled * Fix UI font atlas being eligible for texture compression --- indra/llrender/llfontbitmapcache.cpp | 2 +- indra/llrender/llimagegl.cpp | 216 +++++++++++++++++++++-------------- indra/llrender/llimagegl.h | 8 +- 3 files changed, 135 insertions(+), 91 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp index 46c2e89797..ee9cfd0719 100644 --- a/indra/llrender/llfontbitmapcache.cpp +++ b/indra/llrender/llfontbitmapcache.cpp @@ -117,7 +117,7 @@ bool LLFontBitmapCache::nextOpenPos(S32 width, S32& pos_x, S32& pos_y, EFontGlyp } // Make corresponding GL image. - mImageGLVec[bitmap_idx].push_back(new LLImageGL(image_raw, false)); + mImageGLVec[bitmap_idx].push_back(new LLImageGL(image_raw, false, false)); LLImageGL* image_gl = getImageGL(bitmap_type, bitmap_num); // Start at beginning of the new image. diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index be30807e7f..d5f7949051 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -277,6 +277,15 @@ S32 LLImageGL::dataFormatBits(S32 dataformat) { switch (dataformat) { + case GL_COMPRESSED_RED: return 8; + case GL_COMPRESSED_RG: return 16; + case GL_COMPRESSED_RGB: return 24; + case GL_COMPRESSED_SRGB: return 32; + case GL_COMPRESSED_RGBA: return 32; + case GL_COMPRESSED_SRGB_ALPHA: return 32; + case GL_COMPRESSED_LUMINANCE: return 8; + case GL_COMPRESSED_LUMINANCE_ALPHA: return 16; + case GL_COMPRESSED_ALPHA: return 8; 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; @@ -288,9 +297,12 @@ S32 LLImageGL::dataFormatBits(S32 dataformat) case GL_ALPHA: return 8; case GL_ALPHA8: return 8; case GL_RED: return 8; + case GL_R8: return 8; case GL_COLOR_INDEX: return 8; case GL_LUMINANCE_ALPHA: return 16; case GL_LUMINANCE8_ALPHA8: return 16; + case GL_RG: return 16; + case GL_RG8: return 16; case GL_RGB: return 24; case GL_SRGB: return 24; case GL_RGB8: return 24; @@ -348,6 +360,7 @@ S32 LLImageGL::dataFormatComponents(S32 dataformat) case GL_RED: return 1; case GL_COLOR_INDEX: return 1; case GL_LUMINANCE_ALPHA: return 2; + case GL_RG: return 2; case GL_RGB: return 3; case GL_SRGB: return 3; case GL_RGBA: return 4; @@ -415,29 +428,29 @@ bool LLImageGL::create(LLPointer& dest, const LLImageRaw* imageraw, b //---------------------------------------------------------------------------- -LLImageGL::LLImageGL(bool usemipmaps) +LLImageGL::LLImageGL(bool usemipmaps/* = true*/, bool allow_compression/* = true*/) : mSaveData(0), mExternalTexture(false) { - init(usemipmaps); + init(usemipmaps, allow_compression); setSize(0, 0, 0); sImageList.insert(this); sCount++; } -LLImageGL::LLImageGL(U32 width, U32 height, U8 components, bool usemipmaps) +LLImageGL::LLImageGL(U32 width, U32 height, U8 components, bool usemipmaps/* = true*/, bool allow_compression/* = true*/) : mSaveData(0), mExternalTexture(false) { llassert( components <= 4 ); - init(usemipmaps); + init(usemipmaps, allow_compression); setSize(width, height, components); sImageList.insert(this); sCount++; } -LLImageGL::LLImageGL(const LLImageRaw* imageraw, bool usemipmaps) +LLImageGL::LLImageGL(const LLImageRaw* imageraw, bool usemipmaps/* = true*/, bool allow_compression/* = true*/) : mSaveData(0), mExternalTexture(false) { - init(usemipmaps); + init(usemipmaps, allow_compression); setSize(0, 0, 0); sImageList.insert(this); sCount++; @@ -454,7 +467,7 @@ LLImageGL::LLImageGL( LLGLenum formatType, LLTexUnit::eTextureAddressMode addressMode) { - init(false); + init(false, true); mTexName = texName; mTarget = target; mComponents = components; @@ -476,7 +489,7 @@ LLImageGL::~LLImageGL() } } -void LLImageGL::init(bool usemipmaps) +void LLImageGL::init(bool usemipmaps, bool allow_compression) { #if LL_IMAGEGL_THREAD_CHECK mActiveThread = LLThread::currentID(); @@ -506,7 +519,7 @@ void LLImageGL::init(bool usemipmaps) mHeight = 0; mCurrentDiscardLevel = -1; - mAllowCompression = true; + mAllowCompression = allow_compression; mTarget = GL_TEXTURE_2D; mBindTarget = LLTexUnit::TT_TEXTURE; @@ -1234,90 +1247,122 @@ void LLImageGL::deleteTextures(S32 numTextures, const U32 *textures) 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; + std::unique_ptr scratch; 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; - } + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + if (gGLManager.mGLVersion >= 3.29f) + { + if (pixformat == GL_ALPHA) + { //GL_ALPHA is deprecated, convert to RGBA + const GLint mask[] = { GL_ZERO, GL_ZERO, GL_ZERO, GL_RED }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask); + pixformat = GL_RED; + intformat = GL_R8; + } - 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]; - } + if (pixformat == GL_LUMINANCE) + { //GL_LUMINANCE is deprecated, convert to GL_RGBA + const GLint mask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask); + pixformat = GL_RED; + intformat = GL_R8; } - pixformat = GL_RGBA; - intformat = GL_RGBA8; + if (pixformat == GL_LUMINANCE_ALPHA) + { //GL_LUMINANCE_ALPHA is deprecated, convert to RGBA + const GLint mask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask); + pixformat = GL_RG; + intformat = GL_RG8; + } } - - 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) + else + { + if (pixformat == GL_ALPHA && pixtype == GL_UNSIGNED_BYTE) + { //GL_ALPHA is deprecated, convert to RGBA + if (pixels != nullptr) { - LLError::LLUserWarningMsg::showOutOfMemory(); - LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) - << " bytes for a manual image W" << width << " H" << height << LL_ENDL; - } + scratch.reset(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]; + 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]; + } - U8* pix = (U8*)&scratch[i]; - pix[0] = pix[1] = pix[2] = lum; - pix[3] = alpha; + pixels = scratch.get(); } - } - pixformat = GL_RGBA; - intformat = GL_RGBA8; - } + 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) + if (pixformat == GL_LUMINANCE_ALPHA && pixtype == GL_UNSIGNED_BYTE) + { //GL_LUMINANCE_ALPHA is deprecated, convert to RGBA + if (pixels != nullptr) { - LLError::LLUserWarningMsg::showOutOfMemory(); - LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) - << " bytes for a manual image W" << width << " H" << height << LL_ENDL; + scratch.reset(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; + } + + pixels = scratch.get(); } - U32 pixel_count = (U32)(width * height); - for (U32 i = 0; i < pixel_count; i++) + 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) { - U8 lum = ((U8*)pixels)[i]; + scratch.reset(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; + } - U8* pix = (U8*)&scratch[i]; - pix[0] = pix[1] = pix[2] = lum; - pix[3] = 255; + pixels = scratch.get(); } + pixformat = GL_RGBA; + intformat = GL_RGB8; } - pixformat = GL_RGBA; - intformat = GL_RGB8; } } @@ -1326,6 +1371,14 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt { switch (intformat) { + case GL_RED: + case GL_R8: + intformat = GL_COMPRESSED_RED; + break; + case GL_RG: + case GL_RG8: + intformat = GL_COMPRESSED_RG; + break; case GL_RGB: case GL_RGB8: intformat = GL_COMPRESSED_RGB; @@ -1354,12 +1407,8 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt 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; + LL_WARNS() << "Could not compress format: " << std::hex << intformat << std::dec << LL_ENDL; break; } } @@ -1375,7 +1424,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt if (!use_sub_image) { LL_PROFILE_ZONE_NAMED("glTexImage2D alloc + copy"); - glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, use_scratch ? scratch : pixels); + glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, pixels); } else { @@ -1385,7 +1434,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, nullptr); } - U8* src = (U8*)(use_scratch ? scratch : pixels); + U8* src = (U8*)(pixels); if (src) { LL_PROFILE_ZONE_NAMED("glTexImage2D copy"); @@ -1395,11 +1444,6 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt alloc_tex_image(width, height, intformat, 1); } stop_glerror(); - - if (use_scratch) - { - delete[] scratch; - } } //create an empty GL texture: just create a texture name diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 9a8d935b89..a8b94bd5b0 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -101,9 +101,9 @@ public: 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, bool allow_compression = true); + LLImageGL(U32 width, U32 height, U8 components, bool usemipmaps = true, bool allow_compression = true); + LLImageGL(const LLImageRaw* imageraw, bool usemipmaps = true, bool allow_compression = 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); @@ -203,7 +203,7 @@ public: LLGLenum getTexTarget()const { return mTarget; } - void init(bool usemipmaps); + void init(bool usemipmaps, bool allow_compression); 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); -- cgit v1.2.3 From 1cc1872c76de0d5f5d6c04c4b510367f9f002d97 Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Thu, 22 Aug 2024 15:59:15 +0200 Subject: #2393 BugSplat Crash #1504586: LLRenderTarget::getTexture(483) --- indra/llrender/llrendertarget.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index f700201ace..21a0820d32 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -475,12 +475,10 @@ void LLRenderTarget::clear(U32 mask_in) U32 LLRenderTarget::getTexture(U32 attachment) const { - if (attachment > mTex.size()-1) - { - LL_ERRS() << "Invalid attachment index." << LL_ENDL; - } - if (mTex.empty()) + if (attachment >= mTex.size()) { + LL_WARNS() << "Invalid attachment index " << attachment << " for size " << mTex.size() << LL_ENDL; + llassert(false); return 0; } return mTex[attachment]; -- cgit v1.2.3 From cbca17825614d18a96994c94ec0c38e9d8e812cf Mon Sep 17 00:00:00 2001 From: Rye Mutt Date: Sat, 24 Aug 2024 00:55:32 -0400 Subject: Add Contrast Adaptive Sharpening post process effect(#2399) --- indra/llrender/llglslshader.cpp | 46 +++++++++++++++++++++++++++++++++++++++++ indra/llrender/llglslshader.h | 2 ++ 2 files changed, 48 insertions(+) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index e76a30a954..daa768e8ab 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -1552,6 +1552,34 @@ void LLGLSLShader::uniform4fv(U32 index, U32 count, const GLfloat* v) } } +void LLGLSLShader::uniform4uiv(U32 index, U32 count, const GLuint* v) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; + llassert(sCurBoundShaderPtr == this); + + if (mProgramObject) + { + if (mUniform.size() <= index) + { + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << index << LL_ENDL; + llassert(false); + return; + } + + if (mUniform[index] >= 0) + { + const auto& iter = mValue.find(mUniform[index]); + LLVector4 vec((F32)v[0], (F32)v[1], (F32)v[2], (F32)v[3]); + if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; + glUniform4uiv(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; @@ -1886,6 +1914,24 @@ void LLGLSLShader::uniform4fv(const LLStaticHashedString& uniform, U32 count, co } } +void LLGLSLShader::uniform4uiv(const LLStaticHashedString& uniform, U32 count, const GLuint* v) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; + GLint location = getUniformLocation(uniform); + + if (location >= 0) + { + LLVector4 vec((F32)v[0], (F32)v[1], (F32)v[2], (F32)v[3]); + const auto& iter = mValue.find(location); + if (iter == mValue.end() || shouldChange(iter->second, vec) || count != 1) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; + glUniform4uiv(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; diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 86e5625dca..f3c41cd819 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -208,6 +208,7 @@ public: 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 uniform4uiv(U32 index, U32 count, const GLuint* 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); @@ -223,6 +224,7 @@ public: 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 uniform4uiv(const LLStaticHashedString& uniform, U32 count, const GLuint* v); void uniformMatrix4fv(const LLStaticHashedString& uniform, U32 count, GLboolean transpose, const GLfloat* v); void setMinimumAlpha(F32 minimum); -- cgit v1.2.3 From 2ca193ce468cd4420e0ed47a7991661d1b1ca635 Mon Sep 17 00:00:00 2001 From: Rye Cogtail Date: Fri, 23 Aug 2024 22:06:28 -0400 Subject: Improve FXAA quality and performance when GL version is greater then 4 Adds gather4 support under GLSL 4.0+ --- indra/llrender/llshadermgr.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 512ef340f9..e9608491a4 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -595,8 +595,6 @@ GLuint LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_lev 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 -- cgit v1.2.3 From 436e986893d6eb9de1c4531540e6e2afe8ec06cb Mon Sep 17 00:00:00 2001 From: Rye Cogtail Date: Fri, 23 Aug 2024 22:05:34 -0400 Subject: Fix mac crash on startup due to missing types from enum --- indra/llrender/llimagegl.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index d5f7949051..03ac10c00a 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -316,8 +316,12 @@ S32 LLImageGL::dataFormatBits(S32 dataformat) case GL_RG16F: return 32; case GL_RGB16F: return 48; case GL_RGBA16F: return 64; + case GL_R32F: return 32; + case GL_RG32F: return 64; + case GL_RGB32F: return 96; + case GL_RGBA32F: return 128; default: - LL_ERRS() << "LLImageGL::Unknown format: " << dataformat << LL_ENDL; + LL_ERRS() << "LLImageGL::Unknown format: " << std::hex << dataformat << std::dec << LL_ENDL; return 0; } } @@ -367,7 +371,7 @@ S32 LLImageGL::dataFormatComponents(S32 dataformat) 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; + LL_ERRS() << "LLImageGL::Unknown format: " << std::hex << dataformat << std::dec << LL_ENDL; return 0; } } -- cgit v1.2.3 From 8406d1052c70d1d9f964ec86f6f5468aa9f57bb1 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Tue, 27 Aug 2024 11:53:54 -0500 Subject: #2428 Fix for crash when applying PBR material (#2430) Also attempt to fix some occasional bad texture memory tracking. --- indra/llrender/llimagegl.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 058afa0cf2..e4b176ff69 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -70,12 +70,15 @@ static U64 sTextureBytes = 0; void LLImageGLMemory::alloc_tex_image(U32 width, U32 height, U32 pixformat) { U32 texUnit = gGL.getCurrentTexUnitIndex(); + llassert(texUnit == 0); // allocations should always be done on tex unit 0 U32 texName = gGL.getTexUnit(texUnit)->getCurrTexture(); U64 size = LLImageGL::dataFormatBytes(pixformat, width, height); llassert(size >= 0); sTexMemMutex.lock(); + + // it is a precondition that no existing allocation exists for this texture llassert(sTextureAllocs.find(texName) == sTextureAllocs.end()); sTextureAllocs[texName] = size; @@ -89,7 +92,7 @@ void LLImageGLMemory::free_tex_image(U32 texName) { sTexMemMutex.lock(); auto iter = sTextureAllocs.find(texName); - if (iter != sTextureAllocs.end()) + if (iter != sTextureAllocs.end()) // sometimes a texName will be "freed" before allocated (e.g. first call to setManualImage for a given texName) { llassert(iter->second <= sTextureBytes); // sTextureBytes MUST NOT go below zero @@ -114,6 +117,7 @@ void LLImageGLMemory::free_tex_images(U32 count, const U32* texNames) void LLImageGLMemory::free_cur_tex_image() { U32 texUnit = gGL.getCurrentTexUnitIndex(); + llassert(texUnit == 0); // frees should always be done on tex unit 0 U32 texName = gGL.getTexUnit(texUnit)->getCurrTexture(); free_tex_image(texName); } @@ -1215,8 +1219,8 @@ void LLImageGL::deleteTextures(S32 numTextures, const U32 *textures) if (!sFreeList[idx].empty()) { - glDeleteTextures((GLsizei) sFreeList[idx].size(), sFreeList[idx].data()); free_tex_images((GLsizei) sFreeList[idx].size(), sFreeList[idx].data()); + glDeleteTextures((GLsizei)sFreeList[idx].size(), sFreeList[idx].data()); sFreeList[idx].resize(0); } } @@ -2405,7 +2409,7 @@ bool LLImageGL::scaleDown(S32 desired_discard) { // use a PBO to downscale the texture U64 size = getBytes(desired_discard); llassert(size <= 2048 * 2048 * 4); // we shouldn't be using this method to downscale huge textures, but it'll work - gGL.getTexUnit(0)->bind(this); + gGL.getTexUnit(0)->bind(this, false, true); if (sScratchPBO == 0) { -- cgit v1.2.3 From f8d48f6a29f312724b826806fdc2f9b5ed85fd13 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 28 Aug 2024 14:49:13 -0500 Subject: #2432 Restore default nametag behavior. (#2444) After instrumenting nametags some more, really UI rendering performance improvements should just focus on LLFontGL::render --- indra/llrender/llgl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index 7959b3bb57..c14efe8ab4 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -2742,7 +2742,7 @@ LLGLDepthTest::LLGLDepthTest(GLboolean depth_enabled, GLboolean write_enabled, G : mPrevDepthEnabled(sDepthEnabled), mPrevDepthFunc(sDepthFunc), mPrevWriteEnabled(sWriteEnabled) { stop_glerror(); - + LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE; checkState(); if (!depth_enabled) @@ -2775,6 +2775,7 @@ LLGLDepthTest::LLGLDepthTest(GLboolean depth_enabled, GLboolean write_enabled, G LLGLDepthTest::~LLGLDepthTest() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE; checkState(); if (sDepthEnabled != mPrevDepthEnabled ) { -- cgit v1.2.3 From 3a7374892945a233a9eaa3a88c94a984873373d3 Mon Sep 17 00:00:00 2001 From: Rye Mutt Date: Thu, 29 Aug 2024 15:56:28 -0400 Subject: Remove dead texunit colorspace conversion code --- indra/llrender/llglslshader.cpp | 14 ++++++-------- indra/llrender/llglslshader.h | 8 ++++---- indra/llrender/llrender.cpp | 18 ++---------------- indra/llrender/llrender.h | 5 ----- indra/llrender/llrendertarget.cpp | 1 - 5 files changed, 12 insertions(+), 34 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index daa768e8ab..a157bfee21 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -1087,17 +1087,17 @@ void LLGLSLShader::unbind(void) sCurBoundShaderPtr = NULL; } -S32 LLGLSLShader::bindTexture(const std::string& uniform, LLTexture* texture, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace colorspace) +S32 LLGLSLShader::bindTexture(const std::string& uniform, LLTexture* texture, LLTexUnit::eTextureType mode) { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; S32 channel = 0; channel = getUniformLocation(uniform); - return bindTexture(channel, texture, mode, colorspace); + return bindTexture(channel, texture, mode); } -S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace colorspace) +S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextureType mode) { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; @@ -1113,7 +1113,6 @@ S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextu if (uniform > -1) { gGL.getTexUnit(uniform)->bindFast(texture); - gGL.getTexUnit(uniform)->setTextureColorSpace(colorspace); } return uniform; @@ -1194,7 +1193,7 @@ S32 LLGLSLShader::getTextureChannel(S32 uniform) const return mTexture[uniform]; } -S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace space) +S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode) { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; @@ -1211,12 +1210,11 @@ S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTex { 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) +S32 LLGLSLShader::disableTexture(S32 uniform, LLTexUnit::eTextureType mode) { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; @@ -1229,7 +1227,7 @@ S32 LLGLSLShader::disableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTe 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 (gDebugGL && gGL.getTexUnit(index)->getCurrType() != mode) { if (gDebugSession) { diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index f3c41cd819..913feb6a3f 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -249,16 +249,16 @@ public: //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); + S32 enableTexture(S32 uniform, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); + S32 disableTexture(S32 uniform, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); // 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, LLTexture* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); + S32 bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); 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); diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index 1301d325a0..3572419a7a 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -115,7 +115,7 @@ static const GLenum sGLBlendFactor[] = LLTexUnit::LLTexUnit(S32 index) : mCurrTexType(TT_NONE), - mCurrColorScale(1), mCurrAlphaScale(1), mCurrTexture(0), mTexColorSpace(TCS_LINEAR), + mCurrColorScale(1), mCurrAlphaScale(1), mCurrTexture(0), mHasMipMaps(false), mIndex(index) { @@ -145,8 +145,6 @@ void LLTexUnit::refreshState(void) { glBindTexture(GL_TEXTURE_2D, 0); } - - setTextureColorSpace(mTexColorSpace); } void LLTexUnit::activate(void) @@ -241,7 +239,6 @@ bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind) setTextureAddressMode(gl_tex->mAddressMode); setTextureFilteringOption(gl_tex->mFilterOption); } - setTextureColorSpace(mTexColorSpace); } } else @@ -318,7 +315,6 @@ bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind, S32 setTextureFilteringOption(texture->mFilterOption); stop_glerror(); } - setTextureColorSpace(mTexColorSpace); } stop_glerror(); @@ -354,7 +350,6 @@ bool LLTexUnit::bind(LLCubeMap* cubeMap) setTextureAddressMode(cubeMap->mImages[0]->mAddressMode); setTextureFilteringOption(cubeMap->mImages[0]->mFilterOption); } - setTextureColorSpace(mTexColorSpace); return true; } else @@ -403,7 +398,6 @@ bool LLTexUnit::bindManual(eTextureType type, U32 texture, bool hasMips) mCurrTexture = texture; glBindTexture(sGLTextureType[type], texture); mHasMipMaps = hasMips; - setTextureColorSpace(mTexColorSpace); } return true; } @@ -424,8 +418,6 @@ void LLTexUnit::unbind(eTextureType 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); @@ -447,8 +439,6 @@ void LLTexUnit::unbindFast(eTextureType 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); @@ -642,11 +632,6 @@ void LLTexUnit::debugTextureUnit(void) } } -void LLTexUnit::setTextureColorSpace(eTextureColorSpace space) -{ - mTexColorSpace = space; -} - LLLightState::LLLightState(S32 index) : mIndex(index), mEnabled(false), @@ -1634,6 +1619,7 @@ void LLRender::flush() { HBXXH64 hash; + U32 attribute_mask = LLGLSLShader::sCurBoundShaderPtr->mAttributeMask; { diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h index 010ab122b6..4aa33b7427 100644 --- a/indra/llrender/llrender.h +++ b/indra/llrender/llrender.h @@ -219,17 +219,12 @@ public: void setHasMipMaps(bool hasMips) { mHasMipMaps = hasMips; } - void setTextureColorSpace(eTextureColorSpace space); - - eTextureColorSpace getCurrColorSpace() { return mTexColorSpace; } - protected: friend class LLRender; S32 mIndex; U32 mCurrTexture; eTextureType mCurrTexType; - eTextureColorSpace mTexColorSpace; S32 mCurrColorScale; S32 mCurrAlphaScale; bool mHasMipMaps; diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 21a0820d32..38bc5ff331 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -509,7 +509,6 @@ void LLRenderTarget::bindTexture(U32 index, S32 channel, LLTexUnit::eTextureFilt } gGL.getTexUnit(channel)->setTextureFilteringOption(filter_options); - gGL.getTexUnit(channel)->setTextureColorSpace(isSRGB ? LLTexUnit::TCS_SRGB : LLTexUnit::TCS_LINEAR); } void LLRenderTarget::flush() -- cgit v1.2.3 From 958afaa7a7bfb1217bbffee3fe562f21901277ac Mon Sep 17 00:00:00 2001 From: Rye Mutt Date: Tue, 27 Aug 2024 22:47:32 -0400 Subject: Integrate SMAA and rework post process chain for better visual quality Add SMAA buffer generation passes Add quality levels for both FXAA and SMAA Separate gamma correction and tonemapping for effects that require linear-but-tonemapped inputs Move application of noise to final render pass to screen to avoid damaging other post process effects --- indra/llrender/llglslshader.h | 4 ++++ indra/llrender/llrender.cpp | 1 - indra/llrender/llshadermgr.cpp | 14 ++++++++++++++ indra/llrender/llshadermgr.h | 5 +++++ 4 files changed, 23 insertions(+), 1 deletion(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 913feb6a3f..27c8f0b7d0 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -241,6 +241,10 @@ public: void clearPermutations(); void addPermutation(std::string name, std::string value); + void addPermutations(const std::map& defines) + { + mDefines.insert(defines.begin(), defines.end()); + } void removePermutation(std::string name); void addConstant(const LLGLSLShader::eShaderConsts shader_const); diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index 3572419a7a..7a52f9cfb5 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -1619,7 +1619,6 @@ void LLRender::flush() { HBXXH64 hash; - U32 attribute_mask = LLGLSLShader::sCurBoundShaderPtr->mAttributeMask; { diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index e9608491a4..150277c8df 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -597,6 +597,15 @@ GLuint LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_lev } } + if (type == GL_FRAGMENT_SHADER) + { + extra_code_text[extra_code_count++] = strdup("#define FRAGMENT_SHADER 1\n"); + } + else + { + extra_code_text[extra_code_count++] = strdup("#define VERTEX_SHADER 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 @@ -1468,6 +1477,11 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("debug_normal_draw_length"); + mReservedUniforms.push_back("edgesTex"); + mReservedUniforms.push_back("areaTex"); + mReservedUniforms.push_back("searchTex"); + mReservedUniforms.push_back("blendTex"); + llassert(mReservedUniforms.size() == END_RESERVED_UNIFORMS); std::set dupe_check; diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 2b76ef664b..0eb9db6715 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -332,6 +332,11 @@ public: DEBUG_NORMAL_DRAW_LENGTH, // "debug_normal_draw_length" + SMAA_EDGE_TEX, // "edgesTex" + SMAA_AREA_TEX, // "areaTex" + SMAA_SEARCH_TEX, // "searchTex" + SMAA_BLEND_TEX, // "blendTex" + END_RESERVED_UNIFORMS } eGLSLReservedUniforms; // clang-format on -- cgit v1.2.3 From 4ae1de1f8a78d795958d67afab8356f9a13f707d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 28 Aug 2024 23:05:58 +0300 Subject: viewer#2411 LLFontGL::render optimizations --- indra/llrender/CMakeLists.txt | 2 + indra/llrender/llfontgl.cpp | 124 +++++++++++++++++---- indra/llrender/llfontgl.h | 15 ++- indra/llrender/llfontvertexbuffer.cpp | 157 +++++++++++++++++++++++++++ indra/llrender/llfontvertexbuffer.h | 85 +++++++++++++++ indra/llrender/llrender.cpp | 197 ++++++++++++++++++++++++++++------ indra/llrender/llrender.h | 10 ++ 7 files changed, 538 insertions(+), 52 deletions(-) create mode 100644 indra/llrender/llfontvertexbuffer.cpp create mode 100644 indra/llrender/llfontvertexbuffer.h (limited to 'indra/llrender') diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt index 7f881c8bb3..ccff7c7a8c 100644 --- a/indra/llrender/CMakeLists.txt +++ b/indra/llrender/CMakeLists.txt @@ -17,6 +17,7 @@ set(llrender_SOURCE_FILES llfontfreetype.cpp llfontfreetypesvg.cpp llfontgl.cpp + llfontvertexbuffer.cpp llfontregistry.cpp llgl.cpp llglslshader.cpp @@ -43,6 +44,7 @@ set(llrender_HEADER_FILES llcubemap.h llcubemaparray.h llfontgl.h + llfontvertexbuffer.h llfontfreetype.h llfontfreetypesvg.h llfontbitmapcache.h diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index b6cdb81b33..481e35c16a 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -34,6 +34,7 @@ #include "llfontbitmapcache.h" #include "llfontregistry.h" #include "llgl.h" +#include "llglslshader.h" #include "llimagegl.h" #include "llrender.h" #include "llstl.h" @@ -41,6 +42,7 @@ #include "lltexture.h" #include "lldir.h" #include "llstring.h" +#include "llvertexbuffer.h" // Third party library includes #include @@ -143,7 +145,8 @@ 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, bool use_color) const + ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, bool use_ellipses, bool use_color, + std::list *buffer_list) const { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; @@ -157,6 +160,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons return 0; } + gGL.flush(); // deliberately empty pending verts gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); @@ -270,10 +274,10 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons 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]; + static constexpr S32 GLYPH_BATCH_SIZE = 30; + static thread_local LLVector3 vertices[GLYPH_BATCH_SIZE * 4]; + static thread_local LLVector2 uvs[GLYPH_BATCH_SIZE * 4]; + static thread_local LLColor4U colors[GLYPH_BATCH_SIZE * 4]; LLColor4U text_color(color); // Preserve the transparency to render fading emojis in fading text (e.g. @@ -282,6 +286,9 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons std::pair bitmap_entry = std::make_pair(EFontGlyphType::Grayscale, -1); S32 glyph_count = 0; + S32 buffer_count = 0; + LLVertexBuffer* vb; + LLImageGL* font_image = nullptr; for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; @@ -305,16 +312,35 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons // otherwise the queued glyphs will be taken from wrong textures. if (glyph_count > 0) { - gGL.begin(LLRender::QUADS); + if (buffer_list) { + vb = gGL.beginNoCache(LLRender::QUADS, buffer_count); + if (vb) + { + buffer_list->emplace_back(vb, font_image, LLRender::QUADS, buffer_count); + } + gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); + + vb = gGL.getBuffer(buffer_count); // instead of endNoCache to draw now + if (vb) + { + buffer_list->emplace_back(vb, font_image, LLRender::QUADS, buffer_count); + } + } + else + { + gGL.begin(LLRender::QUADS); + { + gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); + } + gGL.end(); } - gGL.end(); glyph_count = 0; } bitmap_entry = next_bitmap_entry; - LLImageGL* font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second); + font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second); gGL.getTexUnit(0)->bind(font_image); } @@ -338,11 +364,28 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons if (glyph_count >= GLYPH_BATCH_SIZE) { - gGL.begin(LLRender::QUADS); + if (buffer_list) { + vb = gGL.beginNoCache(LLRender::QUADS, buffer_count); + if (vb) + { + buffer_list->emplace_back(vb, font_image, LLRender::QUADS, buffer_count); + } gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); + vb = gGL.endNoCache(buffer_count); + if (vb) + { + buffer_list->emplace_back(vb, font_image, LLRender::QUADS, buffer_count); + } + } + else + { + gGL.begin(LLRender::QUADS); + { + gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); + } + gGL.end(); } - gGL.end(); glyph_count = 0; } @@ -376,11 +419,28 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons cur_render_y = cur_y; } - gGL.begin(LLRender::QUADS); + if (buffer_list) { + vb = gGL.beginNoCache(LLRender::QUADS, buffer_count); + if (vb) + { + buffer_list->emplace_back(vb, font_image, LLRender::QUADS, buffer_count); + } gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); + vb = gGL.endNoCache(buffer_count); + if (vb) + { + buffer_list->emplace_back(vb, font_image, LLRender::QUADS, buffer_count); + } + } + else + { + gGL.begin(LLRender::QUADS); + { + gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); + } + gGL.end(); } - gGL.end(); if (right_x) @@ -394,15 +454,42 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons 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 (buffer_list) + { + vb = gGL.beginNoCache(LLRender::LINES, buffer_count); + if (vb) + { + buffer_list->emplace_back(vb, nullptr, LLRender::QUADS, buffer_count); + } + + gGL.vertex2f(start_x, cur_y - descender); + gGL.vertex2f(cur_x, cur_y - descender); + + vb = gGL.getBuffer(buffer_count); + if (vb) + { + buffer_list->emplace_back(vb, nullptr, LLRender::LINES, buffer_count); + } + } + else + { + gGL.begin(LLRender::LINES); + gGL.vertex2f(start_x, cur_y - descender); + gGL.vertex2f(cur_x, cur_y - descender); + gGL.end(); + } + } + else if (buffer_list) + { + vb = gGL.getBuffer(buffer_count); + if (vb) + { + buffer_list->emplace_back(vb, font_image, gGL.getMode(), buffer_count); + } } if (draw_ellipses) { - // recursively render ellipses at end of string // we've already reserved enough room gGL.pushUIMatrix(); @@ -417,7 +504,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons S32_MAX, max_pixels, right_x, false, - use_color); + use_color, + buffer_list); gGL.popUIMatrix(); } diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index de7529a583..5015601cf6 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -33,6 +33,7 @@ #include "llimagegl.h" #include "llpointer.h" #include "llrect.h" +#include "llvertexbuffer.h" #include "v2math.h" class LLColor4; @@ -42,6 +43,7 @@ class LLFontFreetype; // Structure used to store previously requested fonts. class LLFontRegistry; +class LLVertexBuffer; class LLFontGL { @@ -79,6 +81,16 @@ public: DROP_SHADOW_SOFT }; + struct LLVertexBufferData + { + LLVertexBufferData() : mBuffer(nullptr), mImage(nullptr), mMode(0), mCount(0) {} + LLVertexBufferData(LLVertexBuffer* buffer, LLImageGL* image, U8 mode, U32 count) : mBuffer(buffer), mImage(image), mMode(mode), mCount(count) {} + LLPointer mBuffer; + LLPointer mImage; // might be a better idea to store + U8 mMode; + U32 mCount; + }; + LLFontGL(); ~LLFontGL(); @@ -119,7 +131,8 @@ public: S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX, F32* right_x=NULL, bool use_ellipses = false, - bool use_color = true) const; + bool use_color = true, + std::list* buffer_list = nullptr) const; S32 render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const; diff --git a/indra/llrender/llfontvertexbuffer.cpp b/indra/llrender/llfontvertexbuffer.cpp new file mode 100644 index 0000000000..3ff0771795 --- /dev/null +++ b/indra/llrender/llfontvertexbuffer.cpp @@ -0,0 +1,157 @@ +/** + * @file llfontvertexbuffer.cpp + * @brief Buffer storage for font rendering. + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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 "llfontvertexbuffer.h" + +#include "llvertexbuffer.h" + + +LLFontVertexBuffer::LLFontVertexBuffer(bool track_changes) +: mTrackStringChanges(track_changes) +{ +} + +LLFontVertexBuffer::~LLFontVertexBuffer() +{ + reset(); +} + +void LLFontVertexBuffer::reset() +{ + mBufferList.clear(); +} + +S32 LLFontVertexBuffer::render( + const LLFontGL* fontp, + const LLWString& text, + S32 begin_offset, + F32 x, F32 y, + const LLColor4& color, + LLFontGL::HAlign halign, LLFontGL::VAlign valign, + U8 style, LLFontGL::ShadowType shadow, + S32 max_chars , S32 max_pixels, + F32* right_x, + bool use_ellipses, + bool use_color ) +{ + if (mBufferList.empty()) + { + genBuffers(fontp, text, begin_offset, x, y, color, halign, valign, + style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color); + } + else if (mLastX != x || mLastY != y || mLastColor != color) // always track position and alphs + { + genBuffers(fontp, text, begin_offset, x, y, color, halign, valign, + style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color); + } + else if (true //mTrackStringChanges + && (mLastOffset != begin_offset + || mLastMaxChars != max_chars + || mLastMaxPixels != max_pixels + || mLastStringHash != sStringHasher._Do_hash(text))) // todo, track all parameters? + { + genBuffers(fontp, text, begin_offset, x, y, color, halign, valign, + style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color); + } + else + { + + gGL.flush(); // deliberately empty pending verts + gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); + gGL.pushUIMatrix(); + + gGL.loadUIIdentity(); + + // Depth translation, so that floating text appears 'in-world' + // and is correctly occluded. + gGL.translatef(0.f, 0.f, LLFontGL::sCurDepth); + + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + for (auto &buf_data : mBufferList) + { + if (buf_data.mImage) + { + gGL.getTexUnit(0)->bind(buf_data.mImage); + } + else + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + } + buf_data.mBuffer->setBuffer(); + + if (LLRender::sGLCoreProfile && buf_data.mMode == LLRender::QUADS) + { + buf_data.mBuffer->drawArrays(LLRender::TRIANGLES, 0, buf_data.mCount); + } + else + { + buf_data.mBuffer->drawArrays(buf_data.mMode, 0, buf_data.mCount); + } + } + if (right_x) + { + *right_x = mLastRightX; + } + + gGL.popUIMatrix(); + } + return mChars; +} + +void LLFontVertexBuffer::genBuffers( + const LLFontGL* fontp, + const LLWString& text, + S32 begin_offset, + F32 x, F32 y, + const LLColor4& color, + LLFontGL::HAlign halign, LLFontGL::VAlign valign, + U8 style, LLFontGL::ShadowType shadow, + S32 max_chars, S32 max_pixels, + F32* right_x, + bool use_ellipses, + bool use_color) +{ + mBufferList.clear(); + mChars = fontp->render(text, begin_offset, x, y, color, halign, valign, + style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color, &mBufferList); + + mLastOffset = begin_offset; + mLastMaxChars = max_chars; + mLastMaxPixels = max_pixels; + mLastStringHash = sStringHasher._Do_hash(text); + mLastX = x; + mLastY = y; + mLastColor = color; + + if (right_x) + { + mLastRightX = *right_x; + } +} + diff --git a/indra/llrender/llfontvertexbuffer.h b/indra/llrender/llfontvertexbuffer.h new file mode 100644 index 0000000000..bd42cf6c2d --- /dev/null +++ b/indra/llrender/llfontvertexbuffer.h @@ -0,0 +1,85 @@ +/** + * @file llfontgl.h + * @author Andrii Kleshchev + * @brief Buffer storage for font rendering. + * + * $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_LLFONTVERTEXBUFFER_H +#define LL_LLFONTVERTEXBUFFER_H + +#include "llfontgl.h" + +class LLVertexBuffer; + +class LLFontVertexBuffer +{ +public: + LLFontVertexBuffer(bool track_changes = true); + ~LLFontVertexBuffer(); + + void reset(); + + S32 render(const LLFontGL* fontp, + const LLWString& text, + S32 begin_offset, + F32 x, F32 y, + const LLColor4& color, + LLFontGL::HAlign halign = LLFontGL::LEFT, LLFontGL::VAlign valign = LLFontGL::BASELINE, + U8 style = LLFontGL::NORMAL, LLFontGL::ShadowType shadow = LLFontGL::NO_SHADOW, + S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX, + F32* right_x = NULL, + bool use_ellipses = false, + bool use_color = true); + +private: + + void genBuffers(const LLFontGL* fontp, + const LLWString& text, + S32 begin_offset, + F32 x, F32 y, + const LLColor4& color, + LLFontGL::HAlign halign, LLFontGL::VAlign valign, + U8 style, LLFontGL::ShadowType shadow, + S32 max_chars, S32 max_pixels, + F32* right_x, + bool use_ellipses, + bool use_color); + + + std::list mBufferList; + S32 mChars = 0; + S32 mLastOffset = 0; + S32 mLastMaxChars = 0; + S32 mLastMaxPixels = 0; + size_t mLastStringHash = 0; + F32 mLastX = 0.f; + F32 mLastY = 0.f; + LLColor4 mLastColor; + F32 mLastRightX = 0.f; + bool mTrackStringChanges = true; + + static std::hash sStringHasher; +}; + +#endif diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index 7a52f9cfb5..8cd2d9cc15 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -1570,6 +1570,51 @@ void LLRender::end() flush(); } } + +LLVertexBuffer* LLRender::beginNoCache(const GLuint& mode, S32& count) +{ + if (mode != mMode) + { + if (mode == LLRender::QUADS) + { + mQuadCycle = 1; + } + + if (mMode == LLRender::QUADS || + mMode == LLRender::LINES || + mMode == LLRender::TRIANGLES || + mMode == LLRender::POINTS) + { + return getBuffer(count); + } + else if (mCount != 0) + { + LL_ERRS() << "gGL.begin() called redundantly." << LL_ENDL; + } + mMode = mode; + } + return nullptr; +} + +LLVertexBuffer* LLRender::endNoCache(S32& count) +{ + if (mCount == 0) + { + return nullptr; + //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) + { + return getBuffer(count); + } + return nullptr; +} + void LLRender::flush() { STOP_GLERROR; @@ -1664,27 +1709,7 @@ void LLRender::flush() 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->setTexCoord0Data(mTexcoordsp.get()); - } - - if (attribute_mask & LLVertexBuffer::MAP_COLOR) - { - vb->setColorData(mColorsp.get()); - } - -#if LL_DARWIN - vb->unmapBuffer(); -#endif - vb->unbind(); + vb = genBuffer(attribute_mask, count); sVBCache[vhash] = { vb , std::chrono::steady_clock::now() }; @@ -1712,31 +1737,137 @@ void LLRender::flush() } } - vb->setBuffer(); + drawBuffer(vb, mMode, 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; + } + + resetStriders(count); + } +} + +LLVertexBuffer* LLRender::genBuffer(U32 attribute_mask, S32 count) +{ + LLVertexBuffer * vb = new LLVertexBuffer(attribute_mask); + vb->allocateBuffer(count, 0); + + vb->setBuffer(); + + vb->setPositionData((LLVector4a*)mVerticesp.get()); + + if (attribute_mask & LLVertexBuffer::MAP_TEXCOORD0) + { + vb->setTexCoord0Data(mTexcoordsp.get()); + } - if (mMode == LLRender::QUADS && sGLCoreProfile) + if (attribute_mask & LLVertexBuffer::MAP_COLOR) + { + vb->setColorData(mColorsp.get()); + } + +#if LL_DARWIN + vb->unmapBuffer(); +#endif + vb->unbind(); + + return vb; +} + +void LLRender::drawBuffer(LLVertexBuffer* vb, U32 mode, S32 count) +{ + vb->setBuffer(); + + if (mode == LLRender::QUADS && sGLCoreProfile) + { + vb->drawArrays(LLRender::TRIANGLES, 0, count); + mQuadCycle = 1; + } + else + { + vb->drawArrays(mode, 0, count); + } +} + +void LLRender::resetStriders(S32 count) +{ + mVerticesp[0] = mVerticesp[count]; + mTexcoordsp[0] = mTexcoordsp[count]; + mColorsp[0] = mColorsp[count]; + + mCount = 0; +} + +LLVertexBuffer* LLRender::getBuffer(S32 & count) +{ + STOP_GLERROR; + LLVertexBuffer *vb; + 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) + count = mCount; + + if (mMode == LLRender::QUADS && !sGLCoreProfile) + { + if (mCount % 4 != 0) { - vb->drawArrays(LLRender::TRIANGLES, 0, count); - mQuadCycle = 1; + count -= (mCount % 4); + LL_WARNS() << "Incomplete quad requested." << LL_ENDL; } - else + } + + if (mMode == LLRender::TRIANGLES) + { + if (mCount % 3 != 0) { - vb->drawArrays(mMode, 0, count); + 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) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb cache miss"); + + U32 attribute_mask = LLGLSLShader::sCurBoundShaderPtr->mAttributeMask; + vb = genBuffer(attribute_mask, count); + drawBuffer(vb, mMode, 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; + resetStriders(count); } + else + { + count = 0; + } + + return vb; } void LLRender::vertex3f(const GLfloat& x, const GLfloat& y, const GLfloat& z) diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h index 4aa33b7427..5f84a4a275 100644 --- a/indra/llrender/llrender.h +++ b/indra/llrender/llrender.h @@ -417,6 +417,12 @@ public: void begin(const GLuint& mode); void end(); + + LLVertexBuffer* beginNoCache(const GLuint& mode, S32& count); + LLVertexBuffer* endNoCache(S32& count); + LLVertexBuffer* getBuffer(S32& count); + U8 getMode() const { return mMode; } + void vertex2i(const GLint& x, const GLint& y); void vertex2f(const GLfloat& x, const GLfloat& y); void vertex3f(const GLfloat& x, const GLfloat& y, const GLfloat& z); @@ -485,6 +491,10 @@ public: private: friend class LLLightState; + LLVertexBuffer* genBuffer(U32 attribute_mask, S32 count); + void drawBuffer(LLVertexBuffer* vb, U32 mode, S32 count); + void resetStriders(S32 count); + eMatrixMode mMatrixMode; U32 mMatIdx[NUM_MATRIX_MODES]; U32 mMatHash[NUM_MATRIX_MODES]; -- cgit v1.2.3 From a638d9610d9f369eca6dff74e8860ca466c253c7 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 29 Aug 2024 18:49:10 +0300 Subject: viewer#2411 LLFontGL::render optimizations #2 --- indra/llrender/llfontgl.cpp | 3 + indra/llrender/llfontvertexbuffer.cpp | 119 ++++++++++++++++++++++------------ indra/llrender/llfontvertexbuffer.h | 15 ++++- 3 files changed, 92 insertions(+), 45 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 481e35c16a..701ab4d060 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -490,6 +490,9 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons if (draw_ellipses) { + // signal a separate context + buffer_list->emplace_back(nullptr, nullptr, 0, 0); + // recursively render ellipses at end of string // we've already reserved enough room gGL.pushUIMatrix(); diff --git a/indra/llrender/llfontvertexbuffer.cpp b/indra/llrender/llfontvertexbuffer.cpp index 3ff0771795..c4b68c4a6c 100644 --- a/indra/llrender/llfontvertexbuffer.cpp +++ b/indra/llrender/llfontvertexbuffer.cpp @@ -53,7 +53,8 @@ S32 LLFontVertexBuffer::render( F32 x, F32 y, const LLColor4& color, LLFontGL::HAlign halign, LLFontGL::VAlign valign, - U8 style, LLFontGL::ShadowType shadow, + U8 style, + LLFontGL::ShadowType shadow, S32 max_chars , S32 max_pixels, F32* right_x, bool use_ellipses, @@ -64,62 +65,33 @@ S32 LLFontVertexBuffer::render( genBuffers(fontp, text, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color); } - else if (mLastX != x || mLastY != y || mLastColor != color) // always track position and alphs + else if (mLastX != x || mLastY != y + || mLastFont != fontp + || mLastColor != color // alphas change often + || mLastHalign != halign + || mLastValign != valign + || mLastOffset != begin_offset + || mLastMaxChars != max_chars + || mLastMaxPixels != max_pixels + || mLastStyle != style + || mLastShadow != shadow) // ex: buttons change shadow state { genBuffers(fontp, text, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color); } - else if (true //mTrackStringChanges - && (mLastOffset != begin_offset - || mLastMaxChars != max_chars - || mLastMaxPixels != max_pixels - || mLastStringHash != sStringHasher._Do_hash(text))) // todo, track all parameters? + else if (mTrackStringChanges && mLastStringHash != sStringHasher._Do_hash(text)) { genBuffers(fontp, text, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color); } else { + renderBuffers(); - gGL.flush(); // deliberately empty pending verts - gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); - gGL.pushUIMatrix(); - - gGL.loadUIIdentity(); - - // Depth translation, so that floating text appears 'in-world' - // and is correctly occluded. - gGL.translatef(0.f, 0.f, LLFontGL::sCurDepth); - - gGL.setSceneBlendType(LLRender::BT_ALPHA); - - for (auto &buf_data : mBufferList) - { - if (buf_data.mImage) - { - gGL.getTexUnit(0)->bind(buf_data.mImage); - } - else - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } - buf_data.mBuffer->setBuffer(); - - if (LLRender::sGLCoreProfile && buf_data.mMode == LLRender::QUADS) - { - buf_data.mBuffer->drawArrays(LLRender::TRIANGLES, 0, buf_data.mCount); - } - else - { - buf_data.mBuffer->drawArrays(buf_data.mMode, 0, buf_data.mCount); - } - } if (right_x) { *right_x = mLastRightX; } - - gGL.popUIMatrix(); } return mChars; } @@ -141,6 +113,7 @@ void LLFontVertexBuffer::genBuffers( mChars = fontp->render(text, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color, &mBufferList); + mLastFont = fontp; mLastOffset = begin_offset; mLastMaxChars = max_chars; mLastMaxPixels = max_pixels; @@ -148,6 +121,10 @@ void LLFontVertexBuffer::genBuffers( mLastX = x; mLastY = y; mLastColor = color; + mLastHalign = halign; + mLastValign = valign; + mLastStyle = style; + mLastShadow = shadow; if (right_x) { @@ -155,3 +132,61 @@ void LLFontVertexBuffer::genBuffers( } } +void render_buffers(LLFontVertexBuffer::buffer_list_t::iterator iter, LLFontVertexBuffer::buffer_list_t::iterator end) +{ + gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); + gGL.pushUIMatrix(); + + gGL.loadUIIdentity(); + + // Depth translation, so that floating text appears 'in-world' + // and is correctly occluded. + gGL.translatef(0.f, 0.f, LLFontGL::sCurDepth); + + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + while (iter != end) + { + if (iter->mBuffer == nullptr) + { + // elipses indicator + iter++; + break; + } + if (iter->mImage) + { + gGL.getTexUnit(0)->bind(iter->mImage); + } + else + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + } + iter->mBuffer->setBuffer(); + + if (LLRender::sGLCoreProfile && iter->mMode == LLRender::QUADS) + { + iter->mBuffer->drawArrays(LLRender::TRIANGLES, 0, iter->mCount); + } + else + { + iter->mBuffer->drawArrays(iter->mMode, 0, iter->mCount); + } + iter++; + } + + if (iter != end) + { + gGL.pushUIMatrix(); + render_buffers(iter, end); + gGL.popUIMatrix(); + } + + gGL.popUIMatrix(); +} + +void LLFontVertexBuffer::renderBuffers() +{ + gGL.flush(); // deliberately empty pending verts + render_buffers(mBufferList.begin(), mBufferList.end()); +} + diff --git a/indra/llrender/llfontvertexbuffer.h b/indra/llrender/llfontvertexbuffer.h index bd42cf6c2d..458b0a91bb 100644 --- a/indra/llrender/llfontvertexbuffer.h +++ b/indra/llrender/llfontvertexbuffer.h @@ -46,12 +46,14 @@ public: F32 x, F32 y, const LLColor4& color, LLFontGL::HAlign halign = LLFontGL::LEFT, LLFontGL::VAlign valign = LLFontGL::BASELINE, - U8 style = LLFontGL::NORMAL, LLFontGL::ShadowType shadow = LLFontGL::NO_SHADOW, + U8 style = LLFontGL::NORMAL, + LLFontGL::ShadowType shadow = LLFontGL::NO_SHADOW, S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX, F32* right_x = NULL, bool use_ellipses = false, bool use_color = true); + typedef std::list buffer_list_t; private: void genBuffers(const LLFontGL* fontp, @@ -60,15 +62,18 @@ private: F32 x, F32 y, const LLColor4& color, LLFontGL::HAlign halign, LLFontGL::VAlign valign, - U8 style, LLFontGL::ShadowType shadow, + U8 style, + LLFontGL::ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, bool use_ellipses, bool use_color); + void renderBuffers(); - std::list mBufferList; + buffer_list_t mBufferList; S32 mChars = 0; + const LLFontGL *mLastFont = nullptr; S32 mLastOffset = 0; S32 mLastMaxChars = 0; S32 mLastMaxPixels = 0; @@ -76,6 +81,10 @@ private: F32 mLastX = 0.f; F32 mLastY = 0.f; LLColor4 mLastColor; + LLFontGL::HAlign mLastHalign = LLFontGL::LEFT; + LLFontGL::VAlign mLastValign = LLFontGL::BASELINE; + U8 mLastStyle = LLFontGL::NORMAL; + LLFontGL::ShadowType mLastShadow = LLFontGL::NO_SHADOW; F32 mLastRightX = 0.f; bool mTrackStringChanges = true; -- cgit v1.2.3 From 5c64e5e13d9a75cac510aac3128fc6ee780ab243 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 30 Aug 2024 18:51:48 +0300 Subject: viewer#2411 LLFontGL::render optimizations #3 --- indra/llrender/llfontgl.cpp | 118 +++-------------------- indra/llrender/llfontgl.h | 15 +-- indra/llrender/llfontvertexbuffer.cpp | 75 +++++---------- indra/llrender/llfontvertexbuffer.h | 12 ++- indra/llrender/llrender.cpp | 175 +++++++++++++++++++++------------- indra/llrender/llrender.h | 9 +- indra/llrender/llvertexbuffer.cpp | 47 +++++++++ indra/llrender/llvertexbuffer.h | 31 ++++++ 8 files changed, 238 insertions(+), 244 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 701ab4d060..7d74bb3e46 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -42,7 +42,6 @@ #include "lltexture.h" #include "lldir.h" #include "llstring.h" -#include "llvertexbuffer.h" // Third party library includes #include @@ -145,8 +144,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, bool use_color, - std::list *buffer_list) const + ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, bool use_ellipses, bool use_color) const { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; @@ -160,7 +158,6 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons return 0; } - gGL.flush(); // deliberately empty pending verts gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); @@ -286,9 +283,6 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons std::pair bitmap_entry = std::make_pair(EFontGlyphType::Grayscale, -1); S32 glyph_count = 0; - S32 buffer_count = 0; - LLVertexBuffer* vb; - LLImageGL* font_image = nullptr; for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; @@ -312,35 +306,16 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons // otherwise the queued glyphs will be taken from wrong textures. if (glyph_count > 0) { - if (buffer_list) + gGL.begin(LLRender::QUADS); { - vb = gGL.beginNoCache(LLRender::QUADS, buffer_count); - if (vb) - { - buffer_list->emplace_back(vb, font_image, LLRender::QUADS, buffer_count); - } - gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); - - vb = gGL.getBuffer(buffer_count); // instead of endNoCache to draw now - if (vb) - { - buffer_list->emplace_back(vb, font_image, LLRender::QUADS, buffer_count); - } - } - else - { - gGL.begin(LLRender::QUADS); - { - gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); - } - gGL.end(); } + gGL.end(); glyph_count = 0; } bitmap_entry = next_bitmap_entry; - font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second); + LLImageGL* font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second); gGL.getTexUnit(0)->bind(font_image); } @@ -364,28 +339,11 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons if (glyph_count >= GLYPH_BATCH_SIZE) { - if (buffer_list) + gGL.begin(LLRender::QUADS); { - vb = gGL.beginNoCache(LLRender::QUADS, buffer_count); - if (vb) - { - buffer_list->emplace_back(vb, font_image, LLRender::QUADS, buffer_count); - } gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); - vb = gGL.endNoCache(buffer_count); - if (vb) - { - buffer_list->emplace_back(vb, font_image, LLRender::QUADS, buffer_count); - } - } - else - { - gGL.begin(LLRender::QUADS); - { - gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); - } - gGL.end(); } + gGL.end(); glyph_count = 0; } @@ -418,29 +376,11 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons cur_render_x = cur_x; cur_render_y = cur_y; } - - if (buffer_list) + gGL.begin(LLRender::QUADS); { - vb = gGL.beginNoCache(LLRender::QUADS, buffer_count); - if (vb) - { - buffer_list->emplace_back(vb, font_image, LLRender::QUADS, buffer_count); - } gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); - vb = gGL.endNoCache(buffer_count); - if (vb) - { - buffer_list->emplace_back(vb, font_image, LLRender::QUADS, buffer_count); - } - } - else - { - gGL.begin(LLRender::QUADS); - { - gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); - } - gGL.end(); } + gGL.end(); if (right_x) @@ -454,45 +394,14 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons F32 descender = (F32)llfloor(mFontFreetype->getDescenderHeight()); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - if (buffer_list) - { - vb = gGL.beginNoCache(LLRender::LINES, buffer_count); - if (vb) - { - buffer_list->emplace_back(vb, nullptr, LLRender::QUADS, buffer_count); - } - - gGL.vertex2f(start_x, cur_y - descender); - gGL.vertex2f(cur_x, cur_y - descender); - - vb = gGL.getBuffer(buffer_count); - if (vb) - { - buffer_list->emplace_back(vb, nullptr, LLRender::LINES, buffer_count); - } - } - else - { - gGL.begin(LLRender::LINES); - gGL.vertex2f(start_x, cur_y - descender); - gGL.vertex2f(cur_x, cur_y - descender); - gGL.end(); - } - } - else if (buffer_list) - { - vb = gGL.getBuffer(buffer_count); - if (vb) - { - buffer_list->emplace_back(vb, font_image, gGL.getMode(), buffer_count); - } + gGL.begin(LLRender::LINES); + gGL.vertex2f(start_x, cur_y - descender); + gGL.vertex2f(cur_x, cur_y - descender); + gGL.end(); } if (draw_ellipses) { - // signal a separate context - buffer_list->emplace_back(nullptr, nullptr, 0, 0); - // recursively render ellipses at end of string // we've already reserved enough room gGL.pushUIMatrix(); @@ -507,8 +416,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons S32_MAX, max_pixels, right_x, false, - use_color, - buffer_list); + use_color); gGL.popUIMatrix(); } diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 5015601cf6..de7529a583 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -33,7 +33,6 @@ #include "llimagegl.h" #include "llpointer.h" #include "llrect.h" -#include "llvertexbuffer.h" #include "v2math.h" class LLColor4; @@ -43,7 +42,6 @@ class LLFontFreetype; // Structure used to store previously requested fonts. class LLFontRegistry; -class LLVertexBuffer; class LLFontGL { @@ -81,16 +79,6 @@ public: DROP_SHADOW_SOFT }; - struct LLVertexBufferData - { - LLVertexBufferData() : mBuffer(nullptr), mImage(nullptr), mMode(0), mCount(0) {} - LLVertexBufferData(LLVertexBuffer* buffer, LLImageGL* image, U8 mode, U32 count) : mBuffer(buffer), mImage(image), mMode(mode), mCount(count) {} - LLPointer mBuffer; - LLPointer mImage; // might be a better idea to store - U8 mMode; - U32 mCount; - }; - LLFontGL(); ~LLFontGL(); @@ -131,8 +119,7 @@ public: S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX, F32* right_x=NULL, bool use_ellipses = false, - bool use_color = true, - std::list* buffer_list = nullptr) const; + bool use_color = true) const; S32 render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const; diff --git a/indra/llrender/llfontvertexbuffer.cpp b/indra/llrender/llfontvertexbuffer.cpp index c4b68c4a6c..284762dc94 100644 --- a/indra/llrender/llfontvertexbuffer.cpp +++ b/indra/llrender/llfontvertexbuffer.cpp @@ -60,12 +60,17 @@ S32 LLFontVertexBuffer::render( bool use_ellipses, bool use_color ) { + if (!LLFontGL::sDisplayFont) //do not display texts + { + return static_cast(text.length()); + } if (mBufferList.empty()) { genBuffers(fontp, text, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color); } - else if (mLastX != x || mLastY != y + else if (mLastX != x + || mLastY != y || mLastFont != fontp || mLastColor != color // alphas change often || mLastHalign != halign @@ -74,7 +79,10 @@ S32 LLFontVertexBuffer::render( || mLastMaxChars != max_chars || mLastMaxPixels != max_pixels || mLastStyle != style - || mLastShadow != shadow) // ex: buttons change shadow state + || mLastShadow != shadow // ex: buttons change shadow state + || mLastScaleX != LLFontGL::sScaleX + || mLastScaleY != LLFontGL::sScaleY + || mLastOrigin != LLFontGL::sCurOrigin) { genBuffers(fontp, text, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color); @@ -110,8 +118,11 @@ void LLFontVertexBuffer::genBuffers( bool use_color) { mBufferList.clear(); + + gGL.beginList(&mBufferList); mChars = fontp->render(text, begin_offset, x, y, color, halign, valign, - style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color, &mBufferList); + style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color); + gGL.endList(); mLastFont = fontp; mLastOffset = begin_offset; @@ -126,67 +137,25 @@ void LLFontVertexBuffer::genBuffers( mLastStyle = style; mLastShadow = shadow; + mLastScaleX = LLFontGL::sScaleX; + mLastScaleY = LLFontGL::sScaleY; + mLastOrigin = LLFontGL::sCurOrigin; + if (right_x) { mLastRightX = *right_x; } } -void render_buffers(LLFontVertexBuffer::buffer_list_t::iterator iter, LLFontVertexBuffer::buffer_list_t::iterator end) +void LLFontVertexBuffer::renderBuffers() { + gGL.flush(); // deliberately empty pending verts gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); gGL.pushUIMatrix(); - - gGL.loadUIIdentity(); - - // Depth translation, so that floating text appears 'in-world' - // and is correctly occluded. - gGL.translatef(0.f, 0.f, LLFontGL::sCurDepth); - - gGL.setSceneBlendType(LLRender::BT_ALPHA); - - while (iter != end) - { - if (iter->mBuffer == nullptr) - { - // elipses indicator - iter++; - break; - } - if (iter->mImage) - { - gGL.getTexUnit(0)->bind(iter->mImage); - } - else - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } - iter->mBuffer->setBuffer(); - - if (LLRender::sGLCoreProfile && iter->mMode == LLRender::QUADS) - { - iter->mBuffer->drawArrays(LLRender::TRIANGLES, 0, iter->mCount); - } - else - { - iter->mBuffer->drawArrays(iter->mMode, 0, iter->mCount); - } - iter++; - } - - if (iter != end) + for (LLVertexBufferData& buffer : mBufferList) { - gGL.pushUIMatrix(); - render_buffers(iter, end); - gGL.popUIMatrix(); + buffer.draw(); } - gGL.popUIMatrix(); } -void LLFontVertexBuffer::renderBuffers() -{ - gGL.flush(); // deliberately empty pending verts - render_buffers(mBufferList.begin(), mBufferList.end()); -} - diff --git a/indra/llrender/llfontvertexbuffer.h b/indra/llrender/llfontvertexbuffer.h index 458b0a91bb..7c29e1097b 100644 --- a/indra/llrender/llfontvertexbuffer.h +++ b/indra/llrender/llfontvertexbuffer.h @@ -30,7 +30,7 @@ #include "llfontgl.h" -class LLVertexBuffer; +class LLVertexBufferData; class LLFontVertexBuffer { @@ -52,8 +52,6 @@ public: F32* right_x = NULL, bool use_ellipses = false, bool use_color = true); - - typedef std::list buffer_list_t; private: void genBuffers(const LLFontGL* fontp, @@ -71,7 +69,7 @@ private: void renderBuffers(); - buffer_list_t mBufferList; + std::list mBufferList; S32 mChars = 0; const LLFontGL *mLastFont = nullptr; S32 mLastOffset = 0; @@ -86,6 +84,12 @@ private: U8 mLastStyle = LLFontGL::NORMAL; LLFontGL::ShadowType mLastShadow = LLFontGL::NO_SHADOW; F32 mLastRightX = 0.f; + + // LLFontGL's statics + F32 mLastScaleX = 1.f; + F32 mLastScaleY = 1.f; + LLCoordGL mLastOrigin; + bool mTrackStringChanges = true; static std::hash sStringHasher; diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index 8cd2d9cc15..182f61f907 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -77,6 +77,7 @@ struct LLVBCache }; static std::unordered_map sVBCache; +static thread_local std::list *sBufferDataList = nullptr; static const GLenum sGLTextureType[] = { @@ -1528,6 +1529,25 @@ void LLRender::clearErrors() } } +void LLRender::beginList(std::list *list) +{ + flush(); + sBufferDataList = list; +} + +void LLRender::endList() +{ + if (sBufferDataList) + { + flush(); + sBufferDataList = nullptr; + } + else + { + llassert(false); // something failed to provide a list or closed it twice + } +} + void LLRender::begin(const GLuint& mode) { if (mode != mMode) @@ -1663,78 +1683,26 @@ void LLRender::flush() if (mBuffer) { - HBXXH64 hash; - U32 attribute_mask = LLGLSLShader::sCurBoundShaderPtr->mAttributeMask; + LLVertexBuffer *vb; - { - 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; + U32 attribute_mask = LLGLSLShader::sCurBoundShaderPtr->mAttributeMask; - if (cache != sVBCache.end()) + if (sBufferDataList) { - 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(); + vb = genBuffer(attribute_mask, count); + sBufferDataList->emplace_back( + vb, + mMode, + count, + gGL.getTexUnit(0)->mCurrTexture, + mMatrix[MM_MODELVIEW][mMatIdx[MM_MODELVIEW]], + mMatrix[MM_PROJECTION][mMatIdx[MM_PROJECTION]], + mMatrix[MM_TEXTURE0][mMatIdx[MM_TEXTURE0]] + ); } else { - LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb cache miss"); - vb = genBuffer(attribute_mask, count); - - 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 = bufferfromCache(attribute_mask, count); } drawBuffer(vb, mMode, count); @@ -1749,6 +1717,81 @@ void LLRender::flush() } } +LLVertexBuffer* LLRender::bufferfromCache(U32 attribute_mask, U32 count) +{ + LLVertexBuffer *vb = nullptr; + HBXXH64 hash; + + { + 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); + + 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 = genBuffer(attribute_mask, count); + + 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; + } + } + } + } + return vb; +} + LLVertexBuffer* LLRender::genBuffer(U32 attribute_mask, S32 count) { LLVertexBuffer * vb = new LLVertexBuffer(attribute_mask); diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h index 5f84a4a275..aeee8685ab 100644 --- a/indra/llrender/llrender.h +++ b/indra/llrender/llrender.h @@ -50,7 +50,8 @@ class LLVertexBuffer; class LLCubeMap; class LLImageGL; class LLRenderTarget; -class LLTexture ; +class LLTexture; +class LLVertexBufferData; #define LL_MATRIX_STACK_DEPTH 32 @@ -415,6 +416,10 @@ public: void flush(); + // if list is set, will store buffers in list for later use, if list isn't set, will use cache + void beginList(std::list *list); + void endList(); + void begin(const GLuint& mode); void end(); @@ -491,6 +496,7 @@ public: private: friend class LLLightState; + LLVertexBuffer* bufferfromCache(U32 attribute_mask, U32 count); LLVertexBuffer* genBuffer(U32 attribute_mask, S32 count); void drawBuffer(LLVertexBuffer* vb, U32 mode, S32 count); void resetStriders(S32 count); @@ -525,7 +531,6 @@ private: std::vector mUIOffset; std::vector mUIScale; - }; extern F32 gGLModelView[16]; diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 02afcf12c6..11b41730aa 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -570,6 +570,53 @@ public: static LLVBOPool* sVBOPool = nullptr; +void LLVertexBufferData::draw() +{ + if (!mVB) + { + // signal for pushUIMatrix + return; // todo: find a better way? + } + + if (mTexName) + { + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mTexName); + } + else + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + } + + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.pushMatrix(); + gGL.loadMatrix(mModelView.m); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadMatrix(mProjection.m); + gGL.matrixMode(LLRender::MM_TEXTURE0); + gGL.pushMatrix(); + gGL.loadMatrix(mTexture0.m); + + mVB->setBuffer(); + + if (mMode == LLRender::QUADS && LLRender::sGLCoreProfile) + { + mVB->drawArrays(LLRender::TRIANGLES, 0, mCount); + } + else + { + mVB->drawArrays(mMode, 0, mCount); + } + + gGL.popMatrix(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.popMatrix(); +} + +//============================================================================ + //static U64 LLVertexBuffer::getBytesAllocated() { diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index 4ada0c335b..2a4affdc60 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -53,6 +53,37 @@ //============================================================================ // base class class LLPrivateMemoryPool; +class LLVertexBuffer; + +class LLVertexBufferData +{ +public: + LLVertexBufferData() + : mVB(nullptr) + , mMode(0) + , mCount(0) + , mTexName(0) + {} + LLVertexBufferData(LLVertexBuffer* buffer, U8 mode, U32 count, U32 tex_name, glh::matrix4f model_view, glh::matrix4f projection, glh::matrix4f texture0) + : mVB(buffer) + , mMode(mode) + , mCount(count) + , mTexName(tex_name) + , mProjection(model_view) + , mModelView(projection) + , mTexture0(texture0) + {} + void draw(); + LLPointer mVB; + U8 mMode; + U32 mCount; + U32 mTexName; + glh::matrix4f mProjection; + glh::matrix4f mModelView; + glh::matrix4f mTexture0; +}; +typedef std::list buffer_data_list_t; + class LLVertexBuffer final : public LLRefCount { public: -- cgit v1.2.3 From 57ab1a410f9cb3534bb403e034743505758579d8 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 2 Sep 2024 13:46:13 +0300 Subject: viewer#2411 A bit more coverage for font buffer --- indra/llrender/llfontgl.cpp | 3 +- indra/llrender/llfontvertexbuffer.cpp | 55 +++++++++++++++++++++++++++++++++++ indra/llrender/llfontvertexbuffer.h | 26 +++++++++++++++++ indra/llrender/llrender.cpp | 1 + indra/llrender/llvertexbuffer.cpp | 5 ++-- 5 files changed, 87 insertions(+), 3 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 7d74bb3e46..9721b020c7 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -34,7 +34,6 @@ #include "llfontbitmapcache.h" #include "llfontregistry.h" #include "llgl.h" -#include "llglslshader.h" #include "llimagegl.h" #include "llrender.h" #include "llstl.h" @@ -376,6 +375,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons cur_render_x = cur_x; cur_render_y = cur_y; } + gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); @@ -503,6 +503,7 @@ F32 LLFontGL::getWidthF32(const std::string& utf8text, S32 begin_offset, S32 max F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars, bool no_padding) const { + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; F32 cur_x = 0; diff --git a/indra/llrender/llfontvertexbuffer.cpp b/indra/llrender/llfontvertexbuffer.cpp index 284762dc94..417bacd69d 100644 --- a/indra/llrender/llfontvertexbuffer.cpp +++ b/indra/llrender/llfontvertexbuffer.cpp @@ -46,6 +46,60 @@ void LLFontVertexBuffer::reset() mBufferList.clear(); } +S32 LLFontVertexBuffer::render( + const LLFontGL* fontp, + const LLWString& text, + S32 begin_offset, + LLRect rect, + const LLColor4& color, + LLFontGL::HAlign halign, LLFontGL::VAlign valign, + U8 style, + LLFontGL::ShadowType shadow, + S32 max_chars, S32 max_pixels, + F32* right_x, + bool use_ellipses, + bool use_color) +{ + LLRectf rect_float((F32)rect.mLeft, (F32)rect.mTop, (F32)rect.mRight, (F32)rect.mBottom); + return render(fontp, text, begin_offset, rect_float, color, halign, valign, style, shadow, max_chars, right_x, use_ellipses, use_color); +} + +S32 LLFontVertexBuffer::render( + const LLFontGL* fontp, + const LLWString& text, + S32 begin_offset, + LLRectf rect, + const LLColor4& color, + LLFontGL::HAlign halign, LLFontGL::VAlign valign, + U8 style, + LLFontGL::ShadowType shadow, + S32 max_chars, + F32* right_x, + bool use_ellipses, + bool use_color) +{ + F32 x = rect.mLeft; + F32 y = 0.f; + + switch (valign) + { + case LLFontGL::TOP: + y = rect.mTop; + break; + case LLFontGL::VCENTER: + y = rect.getCenterY(); + break; + case LLFontGL::BASELINE: + case LLFontGL::BOTTOM: + y = rect.mBottom; + break; + default: + y = rect.mBottom; + break; + } + return render(fontp, text, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, (S32)rect.getWidth(), right_x, use_ellipses, use_color); +} + S32 LLFontVertexBuffer::render( const LLFontGL* fontp, const LLWString& text, @@ -117,6 +171,7 @@ void LLFontVertexBuffer::genBuffers( bool use_ellipses, bool use_color) { + // todo: add a debug build assert if this triggers too often for to long? mBufferList.clear(); gGL.beginList(&mBufferList); diff --git a/indra/llrender/llfontvertexbuffer.h b/indra/llrender/llfontvertexbuffer.h index 7c29e1097b..d41c6205ac 100644 --- a/indra/llrender/llfontvertexbuffer.h +++ b/indra/llrender/llfontvertexbuffer.h @@ -40,6 +40,32 @@ public: void reset(); + S32 render(const LLFontGL* fontp, + const LLWString& text, + S32 begin_offset, + LLRect rect, + const LLColor4& color, + LLFontGL::HAlign halign = LLFontGL::LEFT, LLFontGL::VAlign valign = LLFontGL::BASELINE, + U8 style = LLFontGL::NORMAL, + LLFontGL::ShadowType shadow = LLFontGL::NO_SHADOW, + S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX, + F32* right_x = NULL, + bool use_ellipses = false, + bool use_color = true); + + S32 render(const LLFontGL* fontp, + const LLWString& text, + S32 begin_offset, + LLRectf rect, + const LLColor4& color, + LLFontGL::HAlign halign = LLFontGL::LEFT, LLFontGL::VAlign valign = LLFontGL::BASELINE, + U8 style = LLFontGL::NORMAL, + LLFontGL::ShadowType shadow = LLFontGL::NO_SHADOW, + S32 max_chars = S32_MAX, + F32* right_x = NULL, + bool use_ellipses = false, + bool use_color = true); + S32 render(const LLFontGL* fontp, const LLWString& text, S32 begin_offset, diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index 182f61f907..b906563b08 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -1531,6 +1531,7 @@ void LLRender::clearErrors() void LLRender::beginList(std::list *list) { + llassert(LLGLSLShader::sCurBoundShaderPtr == &gUIProgram); flush(); sBufferDataList = list; } diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 11b41730aa..156e300853 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -574,8 +574,9 @@ void LLVertexBufferData::draw() { if (!mVB) { - // signal for pushUIMatrix - return; // todo: find a better way? + llassert(false); + // Not supposed to happen, check buffer generation + return; } if (mTexName) -- cgit v1.2.3 From 4cc0edb189c4deaa205f986d20a6959aa83fb25c Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 2 Sep 2024 22:14:42 +0300 Subject: viewer#2411 Cleanup --- indra/llrender/llfontvertexbuffer.cpp | 9 +-- indra/llrender/llfontvertexbuffer.h | 7 +-- indra/llrender/llrender.cpp | 114 ---------------------------------- indra/llrender/llrender.h | 4 +- 4 files changed, 3 insertions(+), 131 deletions(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llfontvertexbuffer.cpp b/indra/llrender/llfontvertexbuffer.cpp index 417bacd69d..96ec94fe0f 100644 --- a/indra/llrender/llfontvertexbuffer.cpp +++ b/indra/llrender/llfontvertexbuffer.cpp @@ -31,8 +31,7 @@ #include "llvertexbuffer.h" -LLFontVertexBuffer::LLFontVertexBuffer(bool track_changes) -: mTrackStringChanges(track_changes) +LLFontVertexBuffer::LLFontVertexBuffer() { } @@ -141,11 +140,6 @@ S32 LLFontVertexBuffer::render( genBuffers(fontp, text, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color); } - else if (mTrackStringChanges && mLastStringHash != sStringHasher._Do_hash(text)) - { - genBuffers(fontp, text, begin_offset, x, y, color, halign, valign, - style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color); - } else { renderBuffers(); @@ -183,7 +177,6 @@ void LLFontVertexBuffer::genBuffers( mLastOffset = begin_offset; mLastMaxChars = max_chars; mLastMaxPixels = max_pixels; - mLastStringHash = sStringHasher._Do_hash(text); mLastX = x; mLastY = y; mLastColor = color; diff --git a/indra/llrender/llfontvertexbuffer.h b/indra/llrender/llfontvertexbuffer.h index d41c6205ac..67cf2ca13c 100644 --- a/indra/llrender/llfontvertexbuffer.h +++ b/indra/llrender/llfontvertexbuffer.h @@ -35,7 +35,7 @@ class LLVertexBufferData; class LLFontVertexBuffer { public: - LLFontVertexBuffer(bool track_changes = true); + LLFontVertexBuffer(); ~LLFontVertexBuffer(); void reset(); @@ -101,7 +101,6 @@ private: S32 mLastOffset = 0; S32 mLastMaxChars = 0; S32 mLastMaxPixels = 0; - size_t mLastStringHash = 0; F32 mLastX = 0.f; F32 mLastY = 0.f; LLColor4 mLastColor; @@ -115,10 +114,6 @@ private: F32 mLastScaleX = 1.f; F32 mLastScaleY = 1.f; LLCoordGL mLastOrigin; - - bool mTrackStringChanges = true; - - static std::hash sStringHasher; }; #endif diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index b906563b08..a40a668a70 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -1592,50 +1592,6 @@ void LLRender::end() } } -LLVertexBuffer* LLRender::beginNoCache(const GLuint& mode, S32& count) -{ - if (mode != mMode) - { - if (mode == LLRender::QUADS) - { - mQuadCycle = 1; - } - - if (mMode == LLRender::QUADS || - mMode == LLRender::LINES || - mMode == LLRender::TRIANGLES || - mMode == LLRender::POINTS) - { - return getBuffer(count); - } - else if (mCount != 0) - { - LL_ERRS() << "gGL.begin() called redundantly." << LL_ENDL; - } - mMode = mode; - } - return nullptr; -} - -LLVertexBuffer* LLRender::endNoCache(S32& count) -{ - if (mCount == 0) - { - return nullptr; - //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) - { - return getBuffer(count); - } - return nullptr; -} - void LLRender::flush() { STOP_GLERROR; @@ -1844,76 +1800,6 @@ void LLRender::resetStriders(S32 count) mCount = 0; } -LLVertexBuffer* LLRender::getBuffer(S32 & count) -{ - STOP_GLERROR; - LLVertexBuffer *vb; - 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) - 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) - { - LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb cache miss"); - - U32 attribute_mask = LLGLSLShader::sCurBoundShaderPtr->mAttributeMask; - vb = genBuffer(attribute_mask, count); - drawBuffer(vb, mMode, 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; - } - - resetStriders(count); - } - else - { - count = 0; - } - - return vb; -} - void LLRender::vertex3f(const GLfloat& x, const GLfloat& y, const GLfloat& z) { //the range of mVerticesp, mColorsp and mTexcoordsp is [0, 4095] diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h index aeee8685ab..39c13e328a 100644 --- a/indra/llrender/llrender.h +++ b/indra/llrender/llrender.h @@ -45,6 +45,7 @@ #include "glh/glh_linear.h" #include +#include class LLVertexBuffer; class LLCubeMap; @@ -423,9 +424,6 @@ public: void begin(const GLuint& mode); void end(); - LLVertexBuffer* beginNoCache(const GLuint& mode, S32& count); - LLVertexBuffer* endNoCache(S32& count); - LLVertexBuffer* getBuffer(S32& count); U8 getMode() const { return mMode; } void vertex2i(const GLint& x, const GLint& y); -- cgit v1.2.3 From 2fc8d5ff3cfa1b9ad00b310cd4a6cdb557b9415c Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 3 Sep 2024 17:22:04 +0300 Subject: viewer#2411 Cleanup #2 --- indra/llrender/llrender.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'indra/llrender') diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index a40a668a70..828a509971 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -1531,6 +1531,10 @@ void LLRender::clearErrors() void LLRender::beginList(std::list *list) { + if (sBufferDataList) + { + LL_ERRS() << "beginList called while another list is open." << LL_ENDL; + } llassert(LLGLSLShader::sCurBoundShaderPtr == &gUIProgram); flush(); sBufferDataList = list; @@ -1545,7 +1549,7 @@ void LLRender::endList() } else { - llassert(false); // something failed to provide a list or closed it twice + llassert(false); // endList called without an open list } } -- cgit v1.2.3