Skip to content
Snippets Groups Projects
llgl.cpp 53 KiB
Newer Older
/** 
 * @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"
Graham Linden's avatar
Graham Linden committed
#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"
#if LL_WINDOWS
#include "lldxhardware.h"
#endif

#ifdef _DEBUG
//#define GL_STATE_VERIFY
#endif

BOOL gClothRipple = FALSE;
BOOL gHeadlessClient = FALSE;
BOOL gGLActive = FALSE;
BOOL gGLDebugLoggingEnabled = TRUE;
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");
David Parks's avatar
David Parks committed
#ifndef APIENTRY
#define APIENTRY
#endif

void APIENTRY gl_debug_callback(GLenum source,
Rye Mutt's avatar
Rye Mutt committed
	GLenum type,
	GLuint id,
	GLenum severity,
	GLsizei length,
	const GLchar* message,
	GLvoid* userParam)
	if (gGLDebugLoggingEnabled)
Rye Mutt's avatar
Rye Mutt committed
		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;
		if (severity == GL_DEBUG_SEVERITY_HIGH)
		{
			LL_ERRS() << "Halting on GL Error" << LL_ENDL;
		}
void parse_glsl_version(S32& major, S32& minor);

void ll_init_fail_log(std::string filename)
{
	gFailLog.open(filename.c_str());
}


void ll_fail(std::string msg)
{
	
	if (gDebugSession)
	{
		std::vector<std::string> lines;

		gFailLog << LLError::utcTime() << " " << msg << std::endl;

		gFailLog << "Stack Trace:" << std::endl;

		ll_get_stack_trace(lines);
		
		for(size_t i = 0; i < lines.size(); ++i)
		{
			gFailLog << lines[i] << std::endl;
		}

		gFailLog << "End of Stack Trace." << std::endl << std::endl;

		gFailLog.flush();
	}
};

void ll_close_fail_log()
{
	gFailLog.close();
}

LLMatrix4 gGLObliqueProjectionInverse;

#define LL_GL_NAME_POOLING 0

std::list<LLGLUpdate*> LLGLUpdate::sGLQ;

LLGLManager gGLManager;

LLGLManager::LLGLManager() :
	mInited(FALSE),
	mIsDisabled(FALSE),

	mHasMultitexture(FALSE),
	mHasAMDAssociations(FALSE),
	mNumTextureUnits(1),
	mHasMipMapGeneration(FALSE),
	mHasCompressedTextures(FALSE),
	mHasFramebufferObject(FALSE),
	mHasBlendFuncSeparate(FALSE),
	mHasVertexBufferObject(FALSE),
	mHasFlushBufferRange(FALSE),
	mHasPBuffer(FALSE),
	mHasShaderObjects(FALSE),
	mHasVertexShader(FALSE),
	mHasFragmentShader(FALSE),
	mNumTextureImageUnits(0),
	mHasOcclusionQuery(FALSE),
	mHasPointParameters(FALSE),
Tofu Linden's avatar
Tofu Linden committed
	mHasDrawBuffers(FALSE),
	mHasTextureRectangle(FALSE),
	mMaxSampleMaskWords(0),
	mMaxColorTextureSamples(0),
	mMaxDepthTextureSamples(0),
	mMaxIntegerSamples(0),

	mHasAnisotropic(FALSE),
	mHasARBEnvCombine(FALSE),
	mHasCubeMap(FALSE),
	mIsATI(FALSE),
	mIsNVIDIA(FALSE),
	mIsIntel(FALSE),
	mIsGF2or4MX(FALSE),
	mIsGF3(FALSE),
	mIsGFFX(FALSE),
	mATIOffsetVerticalLines(FALSE),
	mHasRequirements(TRUE),

	mHasSeparateSpecularColor(FALSE),

	mDebugGPU(FALSE),

	mDriverVersionMajor(1),
	mDriverVersionMinor(0),
	mDriverVersionRelease(0),
	mGLVersion(1.0f),
	mVRAM(0),
	mGLMaxVertexRange(0),
	mGLMaxIndexRange(0)
{
}

//---------------------------------------------------------------------
// Global initialization for GL
//---------------------------------------------------------------------
#if LL_WINDOWS && !LL_MESA_HEADLESS
Rye Mutt's avatar
Rye Mutt committed
void LLGLManager::initWGL(HDC dc)
{
	mHasPBuffer = FALSE;
Rye Mutt's avatar
Rye Mutt committed
	if (!epoxy_has_wgl_extension(dc, "WGL_ARB_pixel_format"))
	{
		LL_WARNS("RenderInit") << "No ARB pixel format extensions" << LL_ENDL;
	}

Rye Mutt's avatar
Rye Mutt committed
	if (epoxy_has_wgl_extension(dc, "WGL_ARB_create_context"))
Rye Mutt's avatar
Rye Mutt committed
		//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
Rye Mutt's avatar
Rye Mutt committed
	mHasAMDAssociations = epoxy_has_wgl_extension(dc, "WGL_AMD_gpu_association");
	if (mHasAMDAssociations)
	{
Rye Mutt's avatar
Rye Mutt committed
		//GLH_EXT_NAME(wglGetGPUIDsAMD) = (PFNWGLGETGPUIDSAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetGPUIDsAMD");
		//GLH_EXT_NAME(wglGetGPUInfoAMD) = (PFNWGLGETGPUINFOAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetGPUInfoAMD");
Rye Mutt's avatar
Rye Mutt committed
	if (epoxy_has_wgl_extension(dc, "WGL_EXT_swap_control"))
Rye Mutt's avatar
Rye Mutt committed
        //GLH_EXT_NAME(wglSwapIntervalEXT) = (PFNWGLSWAPINTERVALEXTPROC)GLH_EXT_GET_PROC_ADDRESS("wglSwapIntervalEXT");
Rye Mutt's avatar
Rye Mutt committed
	if( !epoxy_has_wgl_extension(dc, "WGL_ARB_pbuffer") )
	{
		LL_WARNS("RenderInit") << "No ARB WGL PBuffer extensions" << LL_ENDL;
	}

Rye Mutt's avatar
Rye Mutt committed
	if( !epoxy_has_wgl_extension(dc, "WGL_ARB_render_texture") )
	{
		LL_WARNS("RenderInit") << "No ARB WGL render texture extensions" << LL_ENDL;
	}

Rye Mutt's avatar
Rye Mutt committed
	mHasPBuffer = epoxy_has_wgl_extension(dc, "WGL_ARB_pbuffer") &&
					epoxy_has_wgl_extension(dc, "WGL_ARB_render_texture") &&
					epoxy_has_wgl_extension(dc, "WGL_ARB_pixel_format");

// 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;
	}
	// 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);
David Parks's avatar
David Parks committed

#if LL_DARWIN
		//never use GLSL greater than 1.20 on OSX
		if (mGLSLVersionMajor > 1 || mGLSLVersionMinor >= 30)
		{
			mGLSLVersionMajor = 1;
			mGLSLVersionMinor = 20;
		}
#endif
	if (mGLVersion >= 2.1f && LLImageGL::sCompressTextures)
		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.
	if (mGLVendor.substr(0,4) == "ATI ")
	{
		mGLVendorShort = "ATI";
		mIsATI = TRUE;

#if LL_WINDOWS && !LL_MESA_HEADLESS
		if (mDriverVersionRelease < 3842)
		{
			mATIOffsetVerticalLines = TRUE;
		}
#endif // LL_WINDOWS

#if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS
		// count any pre OpenGL 3.0 implementation as an old driver
		if (mGLVersion < 3.f) 
		{
			mATIOldDriver = TRUE;
		}
#endif // (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS
	}
	else if (mGLVendor.find("NVIDIA ") != std::string::npos)
	{
		mGLVendorShort = "NVIDIA";
		mIsNVIDIA = TRUE;
		if (   mGLRenderer.find("GEFORCE4 MX") != std::string::npos
			|| mGLRenderer.find("GEFORCE2") != std::string::npos
			|| mGLRenderer.find("GEFORCE 2") != std::string::npos
			|| mGLRenderer.find("GEFORCE4 460 GO") != std::string::npos
			|| mGLRenderer.find("GEFORCE4 440 GO") != std::string::npos
			|| mGLRenderer.find("GEFORCE4 420 GO") != std::string::npos)
		{
			mIsGF2or4MX = TRUE;
		}
		else if (mGLRenderer.find("GEFORCE FX") != std::string::npos
				 || mGLRenderer.find("QUADRO FX") != std::string::npos
				 || mGLRenderer.find("NV34") != std::string::npos)
		{
			mIsGFFX = TRUE;
		}
		else if(mGLRenderer.find("GEFORCE3") != std::string::npos)
		{
			mIsGF3 = TRUE;
		}
#if LL_DARWIN
		else if ((mGLRenderer.find("9400M") != std::string::npos)
			  || (mGLRenderer.find("9600M") != std::string::npos)
			  || (mGLRenderer.find("9800M") != std::string::npos))

	}
	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();
	if (mVRAM == 0)
	{
		S32 old_vram = mVRAM;
#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;
				}
			}
			LL_WARNS("RenderInit") << "VRAM Detected (AMDAssociations):" << mVRAM << LL_ENDL;
	if (mHasATIMemInfo && mVRAM == 0)
	{ //ask the gl how much vram is free at startup and attempt to use no more than half of that
		S32 meminfo[4];
		glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, meminfo);

		mVRAM = meminfo[0] / 1024;
		LL_WARNS("RenderInit") << "VRAM Detected (ATIMemInfo):" << mVRAM << LL_ENDL;

	if (mHasNVXMemInfo && mVRAM == 0)
	{
		S32 dedicated_memory;
		glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &dedicated_memory);
		mVRAM = dedicated_memory/1024;
		LL_WARNS("RenderInit") << "VRAM Detected (NVXMemInfo):" << mVRAM << LL_ENDL;
	{
		// 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::getMBVideoMemoryViaDXGI();
		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.
		else
		{
			LL_INFOS("RenderInit") << "Detected VRAM from GL: " << mVRAM << LL_ENDL;
		}
		LL_INFOS("RenderInit") << "Using VRAM value from OS: " << mVRAM << LL_ENDL;
	stop_glerror();

	if (mHasFragmentShader)
	{
		GLint num_tex_image_units;
Rye Mutt's avatar
Rye Mutt committed
		glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &num_tex_image_units);
		mNumTextureImageUnits = llmin(num_tex_image_units, 32);
	}

	LL_INFOS() << "NUM TEX IMAGE UNITS: " << mNumTextureImageUnits << LL_ENDL;

	if (LLRender::sGLCoreProfile)
	{
		mNumTextureUnits = llmin(mNumTextureImageUnits, MAX_GL_TEXTURE_UNITS);
	{
		GLint num_tex_units;		
Rye Mutt's avatar
Rye Mutt committed
		glGetIntegerv(GL_MAX_TEXTURE_UNITS, &num_tex_units);
		mNumTextureUnits = llmin(num_tex_units, (GLint)MAX_GL_TEXTURE_UNITS);
		if (mIsIntel)
		{
			mNumTextureUnits = llmin(mNumTextureUnits, 2);
		}
	}
	else
	{
		mHasRequirements = FALSE;

		// We don't support cards that don't support the GL_ARB_multitexture extension
		LL_WARNS("RenderInit") << "GL Drivers do not support GL_ARB_multitexture" << LL_ENDL;
		return false;
	}
	
David Parks's avatar
David Parks committed

	if (mHasTextureMultisample)
	{
		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);
	}

	if (mHasDebugOutput && gDebugGL)
	{ //setup debug output callback
Rye Mutt's avatar
Rye Mutt committed
		glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, NULL, GL_TRUE);
		glDebugMessageCallback((GLDEBUGPROC) gl_debug_callback, NULL);
		glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
	//HACK always disable texture multisample, use FXAA instead
	mHasTextureMultisample = FALSE;
#if LL_WINDOWS
	if (mIsATI)
	{ //using multisample textures on ATI results in black screen for some reason
		mHasTextureMultisample = FALSE;
	}
	if (mIsIntel && mGLVersion <= 3.f)
	{ //never try to use framebuffer objects on older intel drivers (crashy)
	if (mHasFramebufferObject)
	{
		glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples);
	}
	setToDebugGPU();
	initGLStates();
void LLGLManager::setToDebugGPU()
{
	//"MOBILE INTEL(R) 965 EXPRESS CHIP", 
	if (mGLRenderer.find("INTEL") != std::string::npos && mGLRenderer.find("965") != std::string::npos)
	{
		mDebugGPU = TRUE ;
	}

	return ;
}

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
#if !LL_DARWIN
    if (mGLVersion >= 3.0f && glGetStringi) {
        std::vector<std::string> gl_extension_str_vec;
        GLint n, i;
        glGetIntegerv(GL_NUM_EXTENSIONS, &n);
        for (i = 0; i < n; i++) {
            std::string exten = ll_safe_string((const char *) glGetStringi(GL_EXTENSIONS, i));
            if (!exten.empty())
                info["GLInfo"]["GLExtensions"].append(exten);
        }
    } else
#endif
    {
        std::string all_exts = ll_safe_string((const char *) glGetString(GL_EXTENSIONS));
        boost::char_separator<char> sep(" ");
        boost::tokenizer<boost::char_separator<char> > tok(all_exts, sep);
        for (boost::tokenizer<boost::char_separator<char> >::iterator i = tok.begin(); i != tok.end(); ++i) {
            info["GLInfo"]["GLExtensions"].append(*i);
        }
    }
#endif
}

std::string LLGLManager::getGLInfoString()
{
	std::string info_str;

	if (gHeadlessClient)
	{
		info_str += std::string("GL_VENDOR      ") + HEADLESS_VENDOR_STRING + std::string("\n");
		info_str += std::string("GL_RENDERER    ") + HEADLESS_RENDERER_STRING + std::string("\n");
		info_str += std::string("GL_VERSION     ") + HEADLESS_VERSION_STRING + std::string("\n");
	}
	else
	{
		info_str += std::string("GL_VENDOR      ") + ll_safe_string((const char *)glGetString(GL_VENDOR)) + std::string("\n");
		info_str += std::string("GL_RENDERER    ") + ll_safe_string((const char *)glGetString(GL_RENDERER)) + std::string("\n");
		info_str += std::string("GL_VERSION     ") + ll_safe_string((const char *)glGetString(GL_VERSION)) + std::string("\n");
	}
#if !LL_MESA_HEADLESS
#if !LL_DARWIN
    if (mGLVersion >= 3.0f && glGetStringi) {
        std::stringstream gl_extension_strstrm;
        GLint n, i;
        glGetIntegerv(GL_NUM_EXTENSIONS, &n);
        for (i = 0; i < n; i++) {
            std::string exten = ll_safe_string((const char *) glGetStringi(GL_EXTENSIONS, i));
            if (!exten.empty() && i != n)
                gl_extension_strstrm << exten << "\n";
        }
        info_str += std::string("GL_EXTENSIONS:\n") + gl_extension_strstrm.str() + std::string("\n");
    } else
    {
        std::string all_exts = ll_safe_string((const char *) glGetString(GL_EXTENSIONS));
        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
#if !LL_DARWIN
    if (mGLVersion >= 3.0f && glGetStringi) {
        std::stringstream gl_extension_strstrm;
        GLint n, i;
        glGetIntegerv(GL_NUM_EXTENSIONS, &n);
        for (i = 0; i < n; i++) {
            std::string exten = ll_safe_string((const char *) glGetStringi(GL_EXTENSIONS, i));
            if (!exten.empty() && i != n)
                gl_extension_strstrm << exten << "\n";
        }
        LL_INFOS("RenderInit") << "GL_EXTENSIONS:\n" << gl_extension_strstrm.str() << LL_ENDL;
    }
    else
#endif
    {
        std::string all_exts = ll_safe_string((const char *) glGetString(GL_EXTENSIONS));
        LLStringUtil::replaceChar(all_exts, ' ', '\n');
        LL_INFOS("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::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_MESA_HEADLESS
	mHasMultitexture = TRUE;
# else
	mHasMultitexture = FALSE;
# endif // GL_ARB_multitexture
	mHasARBEnvCombine = TRUE;	
# else
	mHasARBEnvCombine = FALSE;
# endif // GL_ARB_texture_env_combine
	mHasCompressedTextures = TRUE;
# else
	mHasCompressedTextures = FALSE;
# endif // GL_ARB_texture_compression
	mHasVertexBufferObject = TRUE;
# else
	mHasVertexBufferObject = FALSE;
# endif // GL_ARB_vertex_buffer_object
	mHasFramebufferObject = TRUE;
# else
	mHasFramebufferObject = FALSE;
# endif // GL_EXT_framebuffer_object
	mHasDrawBuffers = TRUE;
#else
	mHasDrawBuffers = FALSE;
# endif // GL_ARB_draw_buffers
# if defined(GL_NV_depth_clamp) || defined(GL_ARB_depth_clamp)
	mHasDepthClamp = TRUE;
#else
	mHasDepthClamp = FALSE;
#endif // defined(GL_NV_depth_clamp) || defined(GL_ARB_depth_clamp)
# if GL_EXT_blend_func_separate
	mHasBlendFuncSeparate = TRUE;
#else
	mHasBlendFuncSeparate = FALSE;
# endif // GL_EXT_blend_func_separate
	mHasMipMapGeneration = FALSE;
	mHasSeparateSpecularColor = FALSE;
	mHasAnisotropic = FALSE;
	mHasCubeMap = FALSE;
	mHasOcclusionQuery = FALSE;
	mHasPointParameters = FALSE;
	mHasShaderObjects = FALSE;
	mHasVertexShader = FALSE;
	mHasFragmentShader = FALSE;
	mHasTextureRectangle = FALSE;
Rye Mutt's avatar
Rye Mutt committed
#else // LL_MESA_HEADLESS
	mHasMultitexture = mGLVersion >= 1.3f || epoxy_has_gl_extension("GL_ARB_multitexture");
	mHasATIMemInfo = epoxy_has_gl_extension("GL_ATI_meminfo"); //Basic AMD method, also see mHasAMDAssociations
	mHasNVXMemInfo = epoxy_has_gl_extension("GL_NVX_gpu_memory_info");
	mHasSeparateSpecularColor = mGLVersion >= 1.2f || epoxy_has_gl_extension("GL_EXT_separate_specular_color");
	mHasAnisotropic = mGLVersion >= 4.6f || epoxy_has_gl_extension("GL_EXT_texture_filter_anisotropic");

	mHasCubeMap = mGLVersion >= 1.3f || epoxy_has_gl_extension("GL_ARB_texture_cube_map");
	mHasARBEnvCombine = mGLVersion >= 1.3f || epoxy_has_gl_extension("GL_ARB_texture_env_combine");
	mHasCompressedTextures = mGLVersion >= 1.3f || epoxy_has_gl_extension("GL_ARB_texture_compression");
	mHasOcclusionQuery = mGLVersion >= 1.5f || epoxy_has_gl_extension("GL_ARB_occlusion_query");
	mHasTimerQuery = mGLVersion >= 3.3f || epoxy_has_gl_extension("GL_ARB_timer_query");
	mHasOcclusionQuery2 = mGLVersion >= 3.3f || epoxy_has_gl_extension("GL_ARB_occlusion_query2");
	mHasVertexBufferObject = mGLVersion >= 1.5f || epoxy_has_gl_extension("GL_ARB_vertex_buffer_object");
	mHasVertexArrayObject = mGLVersion >= 3.0f || epoxy_has_gl_extension("GL_ARB_vertex_array_object");
	mHasSync = mGLVersion >= 3.2f || epoxy_has_gl_extension("GL_ARB_sync");
	mHasMapBufferRange = mGLVersion >= 3.0f || epoxy_has_gl_extension("GL_ARB_map_buffer_range");
	mHasFlushBufferRange = epoxy_has_gl_extension("GL_APPLE_flush_buffer_range");
	mHasDepthClamp = mGLVersion >= 3.2f || (epoxy_has_gl_extension("GL_ARB_depth_clamp") || epoxy_has_gl_extension("GL_NV_depth_clamp"));
	// mask out FBO support when packed_depth_stencil isn't there 'cause we need it for LLRenderTarget -Brad
Rye Mutt's avatar
Rye Mutt committed
	mHasFramebufferObject = mGLVersion >= 3.0f || epoxy_has_gl_extension("GL_ARB_framebuffer_object");
Rye Mutt's avatar
Rye Mutt committed
	mHasFramebufferObject = mGLVersion >= 3.0f || (epoxy_has_gl_extension("GL_EXT_framebuffer_object") &&
		epoxy_has_gl_extension("GL_EXT_framebuffer_blit") &&
		epoxy_has_gl_extension("GL_EXT_framebuffer_multisample") &&
		epoxy_has_gl_extension("GL_EXT_packed_depth_stencil"));
Rye Mutt's avatar
Rye Mutt committed
	mHassRGBTexture = mGLVersion >= 2.1f || epoxy_has_gl_extension("GL_EXT_texture_sRGB");
Rye Mutt's avatar
Rye Mutt committed
	mHassRGBFramebuffer = mGLVersion >= 3.0f || epoxy_has_gl_extension("GL_ARB_framebuffer_sRGB");
Rye Mutt's avatar
Rye Mutt committed
	mHassRGBFramebuffer = mGLVersion >= 3.0f || epoxy_has_gl_extension("GL_EXT_framebuffer_sRGB");
Rye Mutt's avatar
Rye Mutt committed
	mHasTexturesRGBDecode = epoxy_has_gl_extension("GL_EXT_texture_sRGB_decode");
Rye Mutt's avatar
Rye Mutt committed
	mHasTexturesRGBDecode = epoxy_has_gl_extension("GL_ARB_texture_sRGB_decode");
	mHasMipMapGeneration = mHasFramebufferObject || mGLVersion >= 1.4f;

Rye Mutt's avatar
Rye Mutt committed
	mHasDrawBuffers = mGLVersion >= 2.0f || epoxy_has_gl_extension("GL_ARB_draw_buffers");
	mHasBlendFuncSeparate = mGLVersion >= 1.4f || epoxy_has_gl_extension("GL_EXT_blend_func_separate");
	mHasTextureRectangle = mGLVersion >= 3.1f || epoxy_has_gl_extension("GL_ARB_texture_rectangle");
	mHasTextureMultisample = mGLVersion >= 3.2f || epoxy_has_gl_extension("GL_ARB_texture_multisample");
	mHasDebugOutput = mGLVersion >= 4.3f || epoxy_has_gl_extension("GL_ARB_debug_output");
	mHasTransformFeedback = mGLVersion >= 4.0f ? TRUE : FALSE;
#if !LL_DARWIN
Rye Mutt's avatar
Rye Mutt committed
	mHasPointParameters = !mIsATI && (mGLVersion >= 1.4f || epoxy_has_gl_extension("GL_ARB_point_parameters"));
Rye Mutt's avatar
Rye Mutt committed
	mHasShaderObjects = mGLVersion >= 2.0f || (epoxy_has_gl_extension("GL_ARB_shader_objects") && (LLRender::sGLCoreProfile || epoxy_has_gl_extension("GL_ARB_shading_language_100")));
	mHasVertexShader = mGLVersion >= 2.0f || (epoxy_has_gl_extension("GL_ARB_vertex_program") && epoxy_has_gl_extension("GL_ARB_vertex_shader")
		&& (LLRender::sGLCoreProfile || epoxy_has_gl_extension("GL_ARB_shading_language_100")));
	mHasFragmentShader = mGLVersion >= 2.0f || (epoxy_has_gl_extension("GL_ARB_fragment_shader") && (LLRender::sGLCoreProfile || epoxy_has_gl_extension("GL_ARB_shading_language_100")));

	mHasTextureSwizzle = mGLVersion >= 3.3f || epoxy_has_gl_extension("GL_ARB_texture_swizzle");
#if LL_LINUX || LL_SOLARIS
	LL_INFOS() << "initExtensions() checking shell variables to adjust features..." << LL_ENDL;
	// Our extension support for the Linux Client is very young with some
	// potential driver gotchas, so offer a semi-secret way to turn it off.
Tofu Linden's avatar
Tofu Linden committed
	if (getenv("LL_GL_NOEXT"))
	{
		//mHasMultitexture = FALSE; // NEEDED!
		mHasARBEnvCombine = FALSE;
		mHasCompressedTextures = FALSE;
		mHasVertexBufferObject = FALSE;
		mHasFramebufferObject = FALSE;
		mHasBlendFuncSeparate = FALSE;
		mHasMipMapGeneration = FALSE;
		mHasSeparateSpecularColor = FALSE;
		mHasAnisotropic = FALSE;
		mHasCubeMap = FALSE;
		mHasOcclusionQuery = FALSE;
		mHasPointParameters = FALSE;
		mHasShaderObjects = FALSE;
		mHasVertexShader = FALSE;
		mHasFragmentShader = FALSE;
		LL_WARNS("RenderInit") << "GL extension support DISABLED via LL_GL_NOEXT" << LL_ENDL;
	}
	else if (getenv("LL_GL_BASICEXT"))	/* Flawfinder: ignore */
	{
		// This switch attempts to turn off all support for exotic
		// extensions which I believe correspond to fatal driver
		// bug reports.  This should be the default until we get a
		// proper blacklist/whitelist on Linux.
		mHasMipMapGeneration = FALSE;
		mHasAnisotropic = FALSE;
		//mHasCubeMap = FALSE; // apparently fatal on Intel 915 & similar
		//mHasOcclusionQuery = FALSE; // source of many ATI system hangs
		mHasShaderObjects = FALSE;
		mHasVertexShader = FALSE;
		mHasFragmentShader = FALSE;
		mHasBlendFuncSeparate = FALSE;
		LL_WARNS("RenderInit") << "GL extension support forced to SIMPLE level via LL_GL_BASICEXT" << LL_ENDL;
	}
	if (getenv("LL_GL_BLACKLIST"))	/* Flawfinder: ignore */
	{
		// This lets advanced troubleshooters disable specific
		// GL extensions to isolate problems with their hardware.
		// SL-28126
		const char *const blacklist = getenv("LL_GL_BLACKLIST");	/* Flawfinder: ignore */
		LL_WARNS("RenderInit") << "GL extension support partially disabled via LL_GL_BLACKLIST: " << blacklist << LL_ENDL;
		if (strchr(blacklist,'a')) mHasARBEnvCombine = FALSE;
		if (strchr(blacklist,'b')) mHasCompressedTextures = FALSE;
		if (strchr(blacklist,'c')) mHasVertexBufferObject = FALSE;
		if (strchr(blacklist,'d')) mHasMipMapGeneration = FALSE;//S
// 		if (strchr(blacklist,'f')) mHasNVVertexArrayRange = FALSE;//S
// 		if (strchr(blacklist,'g')) mHasNVFence = FALSE;//S
		if (strchr(blacklist,'h')) mHasSeparateSpecularColor = FALSE;
		if (strchr(blacklist,'i')) mHasAnisotropic = FALSE;//S
		if (strchr(blacklist,'j')) mHasCubeMap = FALSE;//S
// 		if (strchr(blacklist,'k')) mHasATIVAO = FALSE;//S
		if (strchr(blacklist,'l')) mHasOcclusionQuery = FALSE;
		if (strchr(blacklist,'m')) mHasShaderObjects = FALSE;//S
		if (strchr(blacklist,'n')) mHasVertexShader = FALSE;//S
		if (strchr(blacklist,'o')) mHasFragmentShader = FALSE;//S
		if (strchr(blacklist,'p')) mHasPointParameters = FALSE;//S
		if (strchr(blacklist,'q')) mHasFramebufferObject = FALSE;//S
		if (strchr(blacklist,'r')) mHasDrawBuffers = FALSE;//S
		if (strchr(blacklist,'s')) mHasTextureRectangle = FALSE;
		if (strchr(blacklist,'t')) mHasBlendFuncSeparate = FALSE;//S
		if (strchr(blacklist,'u')) mHasDepthClamp = FALSE;
#endif // LL_LINUX || LL_SOLARIS
	
	if (!mHasMultitexture)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize multitexturing" << LL_ENDL;
	}
	if (!mHasMipMapGeneration)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize mipmap generation" << LL_ENDL;
	}
	if (!mHasARBEnvCombine)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_texture_env_combine" << LL_ENDL;
	}
	if (!mHasSeparateSpecularColor)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize separate specular color" << LL_ENDL;
	}
	if (!mHasAnisotropic)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize anisotropic filtering" << LL_ENDL;
	}
	if (!mHasCompressedTextures)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_texture_compression" << LL_ENDL;
	}
	if (!mHasOcclusionQuery)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_occlusion_query" << LL_ENDL;
	}
	if (!mHasOcclusionQuery2)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_occlusion_query2" << LL_ENDL;
	}
	if (!mHasPointParameters)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_point_parameters" << LL_ENDL;
	}
	if (!mHasShaderObjects)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_shader_objects" << LL_ENDL;
	}
	if (!mHasVertexShader)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_vertex_shader" << LL_ENDL;
	}
	if (!mHasFragmentShader)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_fragment_shader" << LL_ENDL;
	}
	if (!mHasBlendFuncSeparate)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize GL_EXT_blend_func_separate" << LL_ENDL;
	}
	if (!mHasDrawBuffers)
	{
		LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_draw_buffers" << LL_ENDL;
	}

	// Disable certain things due to known bugs
//	if (mIsIntel && mHasMipMapGeneration)
//	{
//		LL_INFOS("RenderInit") << "Disabling mip-map generation for Intel GPUs" << LL_ENDL;
//		mHasMipMapGeneration = FALSE;
//	}
	
	// Misc
	glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, (GLint*) &mGLMaxVertexRange);
	glGetIntegerv(GL_MAX_ELEMENTS_INDICES, (GLint*) &mGLMaxIndexRange);
	glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint*) &mGLMaxTextureSize);