Fixup texture compression.

This commit is contained in:
Shyotl
2019-03-05 03:11:04 -06:00
parent 6dc2e9e6df
commit 5a7e0b05a4
11 changed files with 61 additions and 54 deletions

View File

@@ -505,10 +505,6 @@ LLGLManager::LLGLManager() :
}
std::set<std::string> sGLExtensions;
bool ExtensionExists(std::string ext)
{
return sGLExtensions.find(ext) != sGLExtensions.end();
}
void registerExtension(std::string ext)
{
sGLExtensions.emplace(ext);
@@ -520,22 +516,30 @@ void loadExtensionStrings()
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
boost::char_separator<char> sep(" ");
for (auto& extension : tokenizer(std::string((const char*)glGetString(GL_EXTENSIONS)), sep))
std::string extensions((const char*)glGetString(GL_EXTENSIONS));
for (auto& extension : tokenizer(extensions, sep))
{
registerExtension(extension);
}
#if LL_WINDOWS
PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsString");
PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
if (wglGetExtensionsStringARB)
{
for (auto& extension : tokenizer(std::string((const char*)wglGetExtensionsStringARB(wglGetCurrentDC())), sep))
extensions = std::string(wglGetExtensionsStringARB(wglGetCurrentDC()));
for (auto& extension : tokenizer(extensions, sep))
{
registerExtension(extension);
}
}
#endif
}
bool ExtensionExists(std::string ext)
{
if (sGLExtensions.empty())
loadExtensionStrings();
return sGLExtensions.find(ext) != sGLExtensions.end();
}
//---------------------------------------------------------------------
// Global initialization for GL
@@ -616,15 +620,6 @@ bool LLGLManager::initGL()
}
#endif
}
if (mGLVersion >= 2.1f && mHasCompressedTextures && 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.
@@ -713,10 +708,18 @@ bool LLGLManager::initGL()
}
stop_glerror();
// This is called here because it depends on the setting of mIsGF2or4MX, and sets up mHasMultitexture.
initExtensions();
stop_glerror();
if (mGLVersion >= 2.1f && mHasCompressedTextures && 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;
}
S32 old_vram = mVRAM;
if (mHasATIMemInfo)
@@ -967,7 +970,7 @@ void LLGLManager::initExtensions()
mHasMultitexture = mGLVersion >= 1.3f || ExtensionExists("GL_ARB_multitexture");
mHasATIMemInfo = ExtensionExists("GL_ATI_meminfo");
mHasNVXMemInfo = ExtensionExists("GL_NVX_gpu_memory_info");
mHasCompressedTextures = mGLVersion >= 1.3 || ExtensionExists("GL_ARB_multitexture");
mHasCompressedTextures = mGLVersion >= 1.3 || ExtensionExists("GL_ARB_texture_compression");
mHasAnisotropic = mGLVersion >= 4.6f || ExtensionExists("GL_EXT_texture_filter_anisotropic");
mHasCubeMap = mGLVersion >= 1.3f || ExtensionExists("GL_ARB_texture_cube_map");
mHasARBEnvCombine = mGLVersion >= 2.1f || ExtensionExists("GL_ARB_texture_env_combine");

View File

@@ -97,9 +97,9 @@ protected:
LOG_CLASS(LLGLTexture);
public:
LLGLTexture(BOOL usemipmaps = TRUE, bool allow_compresssion=true);
LLGLTexture(const LLImageRaw* raw, BOOL usemipmaps, bool allow_compresssion=true) ;
LLGLTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps, bool allow_compresssion=true) ;
LLGLTexture(BOOL usemipmaps = TRUE, bool allow_compresssion=false);
LLGLTexture(const LLImageRaw* raw, BOOL usemipmaps, bool allow_compresssion=false) ;
LLGLTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps, bool allow_compresssion=false) ;
virtual void dump(); // debug info to llinfos

View File

@@ -774,7 +774,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
stop_glerror();
}
LLImageGL::setManualImage(mTarget, gl_level, mFormatInternal, w, h, mFormatPrimary, GL_UNSIGNED_BYTE, (GLvoid*)data_in, mAllowCompression);
mIsCompressed = 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);
@@ -819,7 +819,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
glTexParameteri(mTarget, GL_GENERATE_MIPMAP, GL_TRUE);
}
LLImageGL::setManualImage(mTarget, 0, mFormatInternal,
mIsCompressed = LLImageGL::setManualImage(mTarget, 0, mFormatInternal,
w, h,
mFormatPrimary, mFormatType,
data_in, mAllowCompression);
@@ -890,7 +890,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
stop_glerror();
}
LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data, mAllowCompression);
mIsCompressed = LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data, mAllowCompression);
if (m == 0)
{
analyzeAlpha(data_in, w, h);
@@ -947,7 +947,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
stop_glerror();
}
LLImageGL::setManualImage(mTarget, 0, mFormatInternal, w, h,
mIsCompressed = LLImageGL::setManualImage(mTarget, 0, mFormatInternal, w, h,
mFormatPrimary, mFormatType, (GLvoid *)data_in, mAllowCompression);
analyzeAlpha(data_in, w, h);
@@ -1198,9 +1198,10 @@ void LLImageGL::texMemoryDeallocated(const AllocationInfo& entry)
// static
static LLTrace::BlockTimerStatHandle FTM_SET_MANUAL_IMAGE("setManualImage");
void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression)
bool LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression)
{
LL_RECORD_BLOCK_TIME(FTM_SET_MANUAL_IMAGE);
bool compressed = false;
std::vector<U32> scratch;
if (LLRender::sGLCoreProfile)
{
@@ -1296,6 +1297,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
}
if (LLImageGL::sCompressTextures && allow_compression)
{
compressed = true;
switch (intformat)
{
case GL_RED:
@@ -1327,6 +1329,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
intformat = GL_COMPRESSED_ALPHA;
break;
default:
compressed = false;
LL_WARNS() << "Could not compress format: " << std::hex << intformat << std::dec << LL_ENDL;
break;
}
@@ -1335,6 +1338,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
stop_glerror();
glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, pixels);
stop_glerror();
return compressed;
}
//create an empty GL texture: just create a texture name

View File

@@ -135,9 +135,9 @@ public:
static BOOL create(LLPointer<LLImageGL>& dest, const LLImageRaw* imageraw, BOOL usemipmaps = TRUE);
public:
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);
LLImageGL(BOOL usemipmaps = TRUE, bool allow_compression = false);
LLImageGL(U32 width, U32 height, U8 components, BOOL usemipmaps = TRUE, bool allow_compression = false);
LLImageGL(const LLImageRaw* imageraw, BOOL usemipmaps = TRUE, bool allow_compression = false);
protected:
virtual ~LLImageGL();
@@ -152,7 +152,7 @@ public:
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);
static bool setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels = nullptr, bool allow_compression = false);
BOOL createGLTexture() ;
BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, GLTextureName* usename = nullptr, BOOL to_create = TRUE,

View File

@@ -454,7 +454,7 @@ void LLPostProcess::createScreenTextures()
{
mDepthTexture = LLImageGL::createTextureName();
gGL.getTexUnit(0)->bindManual(type, mDepthTexture->getTexName());
LLImageGL::setManualImage(LLTexUnit::getInternalType(type), 0, GL_DEPTH_COMPONENT24, mScreenWidth, mScreenHeight, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false);
LLImageGL::setManualImage(LLTexUnit::getInternalType(type), 0, GL_DEPTH_COMPONENT24, mScreenWidth, mScreenHeight, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT);
stop_glerror();
gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
@@ -479,7 +479,7 @@ void LLPostProcess::createNoiseTexture()
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mNoiseTexture->getTexName());
stop_glerror();
LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_LUMINANCE8, NOISE_SIZE, NOISE_SIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, &buffer[0], false);
LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_LUMINANCE8, NOISE_SIZE, NOISE_SIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, &buffer[0]);
stop_glerror();
gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);

View File

@@ -85,7 +85,7 @@ void LLRenderTarget::resize(U32 resx, U32 resy)
for (U32 i = 0; i < mTex.size(); ++i)
{ //resize color attachments
gGL.getTexUnit(0)->bindManual(mUsage, mTex[i]);
LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, mInternalFormat[i], mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE, NULL, false);
LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, mInternalFormat[i], mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE);
sBytesAllocated += pix_diff*4;
}
@@ -103,9 +103,9 @@ void LLRenderTarget::resize(U32 resx, U32 resy)
gGL.getTexUnit(0)->bindManual(mUsage, mDepth);
U32 internal_type = LLTexUnit::getInternalType(mUsage);
if(!mStencil)
LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false);
LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT);
else
LLImageGL::setManualImage(internal_type, 0, GL_DEPTH24_STENCIL8, mResX, mResY, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL, false);
LLImageGL::setManualImage(internal_type, 0, GL_DEPTH24_STENCIL8, mResX, mResY, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8);
}
sBytesAllocated += pix_diff*4;
@@ -204,7 +204,7 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt)
{
clear_glerror();
LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, color_fmt, mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE, NULL, false);
LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, color_fmt, mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE);
if (glGetError() != GL_NO_ERROR)
{
LL_WARNS() << "Could not allocate color buffer for render target." << LL_ENDL;
@@ -281,9 +281,9 @@ bool LLRenderTarget::allocateDepth()
stop_glerror();
clear_glerror();
if(!mStencil)
LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false);
LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT);
else
LLImageGL::setManualImage(internal_type, 0, GL_DEPTH24_STENCIL8, mResX, mResY, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL, false);
LLImageGL::setManualImage(internal_type, 0, GL_DEPTH24_STENCIL8, mResX, mResY, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8);
gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
}

View File

@@ -12615,7 +12615,7 @@ This should be as low as possible, but too low may break functionality</string>
<key>RenderCompressTextures</key>
<map>
<key>Comment</key>
<string>Enable texture compression on OpenGL 3.0 and later implementations (EXPERIMENTAL, requires restart)</string>
<string>Enable texture compression on OpenGL 2.1 and later implementations (requires restart)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>

View File

@@ -233,7 +233,7 @@ LLViewerFetchedTexture* LLViewerTextureManager::staticCastToFetchedTexture(LLTex
LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(BOOL usemipmaps, BOOL generate_gl_tex)
{
LLPointer<LLViewerTexture> tex = new LLViewerTexture(usemipmaps, false);
LLPointer<LLViewerTexture> tex = new LLViewerTexture(usemipmaps);
if(generate_gl_tex)
{
tex->generateGLTexture();
@@ -243,7 +243,7 @@ LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(BOOL usemipma
}
LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const LLUUID& id, BOOL usemipmaps, BOOL generate_gl_tex)
{
LLPointer<LLViewerTexture> tex = new LLViewerTexture(id, usemipmaps, false);
LLPointer<LLViewerTexture> tex = new LLViewerTexture(id, usemipmaps);
if(generate_gl_tex)
{
tex->generateGLTexture();
@@ -253,13 +253,13 @@ LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const LLUUID&
}
LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const LLImageRaw* raw, BOOL usemipmaps)
{
LLPointer<LLViewerTexture> tex = new LLViewerTexture(raw, usemipmaps, false);
LLPointer<LLViewerTexture> tex = new LLViewerTexture(raw, usemipmaps);
tex->setCategory(LLGLTexture::LOCAL);
return tex;
}
LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps, BOOL generate_gl_tex)
{
LLPointer<LLViewerTexture> tex = new LLViewerTexture(width, height, components, usemipmaps, false);
LLPointer<LLViewerTexture> tex = new LLViewerTexture(width, height, components, usemipmaps);
if(generate_gl_tex)
{
tex->generateGLTexture();
@@ -378,7 +378,7 @@ void LLViewerTextureManager::init()
imagep->setCachedRawImage(0, image_raw);
image_raw = NULL;
#else
LLViewerFetchedTexture::sDefaultImagep = LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLGLTexture::BOOST_UI);
LLViewerFetchedTexture::sDefaultImagep = LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_UI);
#endif
LLViewerFetchedTexture::sDefaultImagep->dontDiscard();
LLViewerFetchedTexture::sDefaultImagep->setCategory(LLGLTexture::OTHER);
@@ -990,7 +990,7 @@ const std::string& fttype_to_string(const FTType& fttype)
//----------------------------------------------------------------------------------------------
LLViewerFetchedTexture::LLViewerFetchedTexture(const LLUUID& id, FTType f_type, const LLHost& host, BOOL usemipmaps)
: LLViewerTexture(id, usemipmaps, f_type != FTT_LOCAL_FILE),
: LLViewerTexture(id, usemipmaps, f_type == FTT_DEFAULT || f_type == FTT_MAP_TILE),
mTargetHost(host)
{
init(TRUE);
@@ -1004,7 +1004,7 @@ LLViewerFetchedTexture::LLViewerFetchedTexture(const LLUUID& id, FTType f_type,
}
LLViewerFetchedTexture::LLViewerFetchedTexture(const LLImageRaw* raw, FTType f_type, BOOL usemipmaps)
: LLViewerTexture(raw, usemipmaps, f_type != FTT_LOCAL_FILE)
: LLViewerTexture(raw, usemipmaps, f_type == FTT_DEFAULT || f_type == FTT_MAP_TILE)
{
init(TRUE);
mFTType = f_type;
@@ -1012,7 +1012,7 @@ LLViewerFetchedTexture::LLViewerFetchedTexture(const LLImageRaw* raw, FTType f_t
}
LLViewerFetchedTexture::LLViewerFetchedTexture(const std::string& url, FTType f_type, const LLUUID& id, BOOL usemipmaps)
: LLViewerTexture(id, usemipmaps, f_type != FTT_LOCAL_FILE),
: LLViewerTexture(id, usemipmaps, f_type == FTT_DEFAULT || f_type == FTT_MAP_TILE),
mUrl(url)
{
init(TRUE);

View File

@@ -119,10 +119,10 @@ public:
static void initClass();
static void updateClass(const F32 velocity, const F32 angular_velocity) ;
LLViewerTexture(BOOL usemipmaps = TRUE, bool allow_compression = true);
LLViewerTexture(const LLUUID& id, BOOL usemipmaps, bool allow_compression = true) ;
LLViewerTexture(const LLImageRaw* raw, BOOL usemipmaps, bool allow_compression = true) ;
LLViewerTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps, bool allow_compression = true) ;
LLViewerTexture(BOOL usemipmaps = TRUE, bool allow_compression = false);
LLViewerTexture(const LLUUID& id, BOOL usemipmaps, bool allow_compression = false) ;
LLViewerTexture(const LLImageRaw* raw, BOOL usemipmaps, bool allow_compression = false) ;
LLViewerTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps, bool allow_compression = false) ;
void setNeedsAlphaAndPickMask(BOOL need_mask) { if(mGLTexturep)mGLTexturep->setNeedsAlphaAndPickMask(need_mask); }

View File

@@ -1146,7 +1146,7 @@ void LLPipeline::createGLBuffers()
mNoiseMap = LLImageGL::createTextureName();
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mNoiseMap->getTexName());
LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_RGB16F_ARB, NOISE_MAP_RES, NOISE_MAP_RES, GL_RGB, GL_FLOAT, noise, false);
LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_RGB16F_ARB, NOISE_MAP_RES, NOISE_MAP_RES, GL_RGB, GL_FLOAT, noise);
gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
}
@@ -1209,7 +1209,7 @@ void LLPipeline::createLUTBuffers()
#endif
mLightFunc = LLImageGL::createTextureName();
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mLightFunc->getTexName());
LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, pix_format, lightResX, lightResY, GL_RED, GL_FLOAT, ls, false);
LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, pix_format, lightResX, lightResY, GL_RED, GL_FLOAT, ls);
//LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_UNSIGNED_BYTE, lightResX, lightResY, GL_RED, GL_UNSIGNED_BYTE, ls, false);
gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_TRILINEAR);

View File

@@ -124,7 +124,7 @@
<combo_item name="VSyncAdaptive" value="-1">Adaptive</combo_item>
</combo_box>
<spinner bottom_delta="70" control_name="TextureMemory" decimal_digits="0" height="16" increment="16" initial_val="32" label="Texture Memory (MB):" label_width="138" left="10" max_val="4096" min_val="0" name="GrapicsCardTextureMemory" tool_tip="Amount of memory to allocate for textures. Defaults to Video Card Memory. Reducing this may improve performance but may also make textures blurry." width="202"/>
<check_box bottom_delta="-18" control_name="RenderCompressTextures" height="16" label="Enable Texture Compression (Requires restart)" left="5" name="compressed_textures"
<check_box bottom_delta="-18" control_name="RenderCompressTextures" height="16" label="Enable Texture Compression (Needs restart)" left="5" name="compressed_textures"
tool_tip="Enabling this reduces GPU memory consumption at the expense of in-world texture quality."/>
<check_box bottom_delta="-25" control_name="RenderVBOEnable" height="16" label="Enable OpenGL Vertex Buffer Objects" left="5" name="vbo"
tool_tip="Enabling this on modern hardware gives a performance gain. However, older hardware often has poor implementations of VBOs and you may get crashes when this is enabled."/>