summaryrefslogtreecommitdiff
path: root/indra/llrender
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llrender')
-rw-r--r--indra/llrender/llglslshader.cpp65
-rw-r--r--indra/llrender/llglslshader.h9
-rw-r--r--indra/llrender/llgltexture.cpp21
-rw-r--r--indra/llrender/llgltexture.h6
-rw-r--r--indra/llrender/llimagegl.cpp260
-rw-r--r--indra/llrender/llimagegl.h35
-rw-r--r--indra/llrender/llrender.cpp2
-rw-r--r--indra/llrender/llrender.h1
-rw-r--r--indra/llrender/llshadermgr.cpp2
-rw-r--r--indra/llrender/llshadermgr.h2
-rw-r--r--indra/llrender/llvertexbuffer.cpp106
-rw-r--r--indra/llrender/llvertexbuffer.h18
12 files changed, 254 insertions, 273 deletions
diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp
index 79979657f1..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*> 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);
@@ -1099,7 +1103,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;
}
@@ -1120,6 +1125,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;
}
@@ -1167,7 +1174,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;
}
@@ -1192,7 +1200,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;
}
@@ -1213,7 +1222,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];
@@ -1244,7 +1254,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;
}
@@ -1269,7 +1280,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;
}
@@ -1304,7 +1316,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;
}
@@ -1330,7 +1343,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;
}
@@ -1356,7 +1370,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;
}
@@ -1382,7 +1397,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;
}
@@ -1408,7 +1424,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;
}
@@ -1435,7 +1452,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;
}
@@ -1461,7 +1479,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;
}
@@ -1487,7 +1506,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;
}
@@ -1513,7 +1533,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;
}
@@ -1540,7 +1561,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;
}
@@ -1560,7 +1582,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;
}
@@ -1580,7 +1603,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;
}
@@ -1600,7 +1624,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/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/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 <unordered_set>
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*> LLImageGL::sImageList;
+std::unordered_set<LLImageGL*> 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<LLImageGL*>::iterator iter = LLImageGL::sImageList.begin();
- iter != LLImageGL::sImageList.end(); iter++)
- {
- LLImageGL* glimage = *iter;
- if (glimage->getTexName() && glimage->isGLTextureCreated())
- {
- gGL.getTexUnit(0)->bind(glimage) ;
- glimage->checkTexSize() ;
- gGL.getTexUnit(0)->unbind(glimage->getTarget()) ;
- }
- }
-}
void LLImageGL::checkTexSize(bool forced) const
{
@@ -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<LLImageGL*>::iterator iter = sImageList.begin();
- iter != sImageList.end(); iter++)
- {
- LLImageGL* glimage = *iter;
- if (glimage->mTexName)
- {
- if (save_state && glimage->isGLTextureCreated() && glimage->mComponents)
- {
- glimage->mSaveData = new LLImageRaw;
- if(!glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData, false)) //necessary, keep it.
- {
- glimage->mSaveData = NULL ;
- }
- }
-
- glimage->destroyGLTexture();
- stop_glerror();
- }
- }
- sAllowReadBackRaw = false ;
-}
-
-//static
-void LLImageGL::restoreGL()
-{
- for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
- iter != sImageList.end(); iter++)
- {
- LLImageGL* glimage = *iter;
- if(glimage->getTexName())
- {
- LL_ERRS() << "tex name is not 0." << LL_ENDL ;
- }
- if (glimage->mSaveData.notNull())
- {
- if (glimage->getComponents() && glimage->mSaveData->getComponents())
- {
- glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData, 0, true, glimage->getCategory());
- stop_glerror();
- }
- glimage->mSaveData = NULL; // deletes data
- }
- }
}
//static
void LLImageGL::dirtyTexOptions()
{
- for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
- iter != sImageList.end(); iter++)
+ 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 <unordered_set>
#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<LLImageGL*> sImageList;
+ static std::unordered_set<LLImageGL*> 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.
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/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
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)
{}