Removes OpenGL driver DLL version from reports on Windows.
Differential Revision: https://code.wildfiregames.com/D5052 This was SVN commit r27718.
This commit is contained in:
parent
e31d70f059
commit
3d071e4649
@ -1,118 +0,0 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* return DLL version information.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
#include "lib/sysdep/os/win/wdll_ver.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/sysdep/os/win/win.h"
|
||||
#include "lib/sysdep/os/win/wutil.h"
|
||||
|
||||
#include "lib/allocators/shared_ptr.h"
|
||||
|
||||
#if MSC_VERSION
|
||||
#pragma comment(lib, "version.lib") // DLL version
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static Status ReadVersionString(const OsPath& modulePathname, wchar_t* out_ver, size_t out_ver_len)
|
||||
{
|
||||
WinScopedPreserveLastError s; // GetFileVersion*, Ver*
|
||||
|
||||
// determine size of and allocate memory for version information.
|
||||
DWORD unused;
|
||||
const DWORD ver_size = GetFileVersionInfoSizeW(OsString(modulePathname).c_str(), &unused); // [bytes]
|
||||
if(!ver_size)
|
||||
{
|
||||
// check if the failure is due to not finding modulePathname
|
||||
// (necessary since GetFileVersionInfoSize doesn't SetLastError)
|
||||
HMODULE hModule = LoadLibraryExW(OsString(modulePathname).c_str(), 0, LOAD_LIBRARY_AS_DATAFILE);
|
||||
if(!hModule)
|
||||
return ERR::FAIL; // NOWARN (file not found - due to FS redirection?)
|
||||
FreeLibrary(hModule);
|
||||
return ERR::NOT_SUPPORTED; // NOWARN (module apparently lacks version information)
|
||||
}
|
||||
|
||||
std::shared_ptr<u8> mem = Allocate(ver_size);
|
||||
if(!GetFileVersionInfoW(OsString(modulePathname).c_str(), 0, ver_size, mem.get()))
|
||||
WARN_RETURN(ERR::_3);
|
||||
|
||||
u16* lang; // -> 16 bit language ID, 16 bit codepage
|
||||
UINT lang_len;
|
||||
const BOOL ok = VerQueryValueW(mem.get(), L"\\VarFileInfo\\Translation", (void**)&lang, &lang_len);
|
||||
if(!ok || !lang || lang_len != 4)
|
||||
WARN_RETURN(ERR::_4);
|
||||
|
||||
wchar_t subblock[64];
|
||||
swprintf_s(subblock, ARRAY_SIZE(subblock), L"\\StringFileInfo\\%04X%04X\\FileVersion", lang[0], lang[1]);
|
||||
const wchar_t* in_ver;
|
||||
UINT in_ver_len;
|
||||
if(!VerQueryValueW(mem.get(), subblock, (void**)&in_ver, &in_ver_len))
|
||||
WARN_RETURN(ERR::_5);
|
||||
|
||||
wcscpy_s(out_ver, out_ver_len, in_ver);
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
void wdll_ver_Append(const OsPath& pathname, VersionList& list)
|
||||
{
|
||||
if(pathname.empty())
|
||||
return; // avoid error in ReadVersionString
|
||||
|
||||
// pathname may not have an extension (e.g. driver names from the
|
||||
// registry). note that always appending ".dll" would be incorrect
|
||||
// since some have ".sys" extension.
|
||||
OsPath modulePathname(pathname);
|
||||
if(modulePathname.Extension() == "")
|
||||
modulePathname = modulePathname.ChangeExtension(L".dll");
|
||||
const OsPath moduleName(modulePathname.Filename());
|
||||
|
||||
// read file version. try this with and without FS redirection since
|
||||
// pathname might assume both.
|
||||
wchar_t versionString[500]; // enclosed in () below
|
||||
if(ReadVersionString(modulePathname, versionString, ARRAY_SIZE(versionString)) != INFO::OK)
|
||||
{
|
||||
WinScopedDisableWow64Redirection s;
|
||||
// still failed; avoid polluting list with DLLs that don't exist
|
||||
// (requiring callers to check for their existence beforehand would be
|
||||
// onerous and unreliable)
|
||||
if(ReadVersionString(modulePathname, versionString, ARRAY_SIZE(versionString)) != INFO::OK)
|
||||
return;
|
||||
}
|
||||
|
||||
if(!list.empty())
|
||||
list += L", ";
|
||||
|
||||
list += moduleName.Filename().string();
|
||||
list += L" (";
|
||||
list += versionString;
|
||||
list += L")";
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* return DLL version information
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_WDLL_VER
|
||||
#define INCLUDED_WDLL_VER
|
||||
|
||||
#include "lib/os_path.h"
|
||||
|
||||
typedef std::wstring VersionList;
|
||||
|
||||
/**
|
||||
* Read DLL version information and append it to a string.
|
||||
*
|
||||
* @param pathname of DLL (preferably the complete path, so that we don't
|
||||
* inadvertently load another one on the library search path.)
|
||||
* If no extension is given, .dll will be appended.
|
||||
*
|
||||
* The text output includes the module name.
|
||||
* On failure, the version is given as "unknown".
|
||||
**/
|
||||
extern void wdll_ver_Append(const OsPath& pathname, VersionList& list);
|
||||
|
||||
#endif // #ifndef INCLUDED_WDLL_VER
|
@ -1,144 +0,0 @@
|
||||
/* Copyright (C) 2021 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* graphics card detection on Windows.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
#include "lib/sysdep/os/win/wgfx.h"
|
||||
|
||||
#include "lib/sysdep/os/win/wdll_ver.h"
|
||||
#include "lib/sysdep/os/win/wutil.h"
|
||||
|
||||
#if MSC_VERSION
|
||||
#pragma comment(lib, "advapi32.lib") // registry
|
||||
#endif
|
||||
|
||||
|
||||
// note: this implementation doesn't require OpenGL to be initialized.
|
||||
static Status AppendDriverVersionsFromRegistry(VersionList& versionList)
|
||||
{
|
||||
// rationale:
|
||||
// - we could easily determine the 2d driver via EnumDisplaySettings,
|
||||
// but we want to query the actual OpenGL driver. see
|
||||
// http://www.opengl.org/discussion_boards/ubb/Forum3/HTML/009679.html ;
|
||||
// in short, we need the exact OpenGL driver version because some
|
||||
// driver packs (e.g. Omega) mix and match DLLs.
|
||||
// - an alternative implementation would be to enumerate all
|
||||
// DLLs loaded into the process, and check for a glBegin export.
|
||||
// that requires toolhelp/PSAPI (a bit complicated) and telling
|
||||
// ICD/opengl32.dll apart (not future-proof).
|
||||
// - therefore, we stick with the OpenGLDrivers approach. since there is
|
||||
// no good way to determine which of the subkeys (e.g. nvoglnt) is
|
||||
// active (several may exist due to previously removed drivers),
|
||||
// we just display all of them. it is obvious from looking at
|
||||
// gfx_card which one is correct; we thus avoid driver-specific
|
||||
// name checks and reporting incorrectly.
|
||||
|
||||
wchar_t dllName[MAX_PATH+1];
|
||||
|
||||
HKEY hkDrivers;
|
||||
const wchar_t* key = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers";
|
||||
// (we've received word of this failing on one WinXP system, but
|
||||
// AppendDriverVersionsFromKnownFiles might still work.)
|
||||
if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hkDrivers) != 0)
|
||||
return ERR::FAIL; // NOWARN (see above)
|
||||
|
||||
// for each subkey (i.e. installed OpenGL driver):
|
||||
for(DWORD i = 0; ; i++)
|
||||
{
|
||||
wchar_t driverName[32];
|
||||
DWORD driverNameLength = ARRAY_SIZE(driverName);
|
||||
const LONG err = RegEnumKeyExW(hkDrivers, i, driverName, &driverNameLength, 0, 0, 0, 0);
|
||||
if(err == ERROR_NO_MORE_ITEMS)
|
||||
{
|
||||
if(i == 0)
|
||||
{
|
||||
RegCloseKey(hkDrivers);
|
||||
return ERR::NOT_SUPPORTED; // NOWARN (ATI and NVidia don't create sub-keys on Windows 7)
|
||||
}
|
||||
break;
|
||||
}
|
||||
ENSURE(err == ERROR_SUCCESS);
|
||||
|
||||
HKEY hkDriver;
|
||||
if(RegOpenKeyExW(hkDrivers, driverName, 0, KEY_QUERY_VALUE, &hkDriver) == 0)
|
||||
{
|
||||
DWORD dllNameLength = ARRAY_SIZE(dllName)-5; // for ".dll"
|
||||
if(RegQueryValueExW(hkDriver, L"Dll", 0, 0, (LPBYTE)dllName, &dllNameLength) == 0)
|
||||
wdll_ver_Append(dllName, versionList);
|
||||
|
||||
RegCloseKey(hkDriver);
|
||||
}
|
||||
}
|
||||
|
||||
// for each value:
|
||||
// (some old drivers, e.g. S3 Super Savage, store their ICD name in a
|
||||
// single REG_SZ value. we therefore include those as well.)
|
||||
for(DWORD i = 0; ; i++)
|
||||
{
|
||||
wchar_t name[100]; // we don't need this, but RegEnumValue fails otherwise.
|
||||
DWORD nameLength = ARRAY_SIZE(name);
|
||||
DWORD type;
|
||||
DWORD dllNameLength = ARRAY_SIZE(dllName)-5; // for ".dll"
|
||||
const DWORD err = RegEnumValueW(hkDrivers, i, name, &nameLength, 0, &type, (LPBYTE)dllName, &dllNameLength);
|
||||
if(err == ERROR_NO_MORE_ITEMS)
|
||||
break;
|
||||
ENSURE(err == ERROR_SUCCESS);
|
||||
if(type == REG_SZ)
|
||||
wdll_ver_Append(dllName, versionList);
|
||||
}
|
||||
|
||||
RegCloseKey(hkDrivers);
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
static void AppendDriverVersionsFromKnownFiles(VersionList& versionList)
|
||||
{
|
||||
// (check all known file names regardless of gfx_card, which may change and
|
||||
// defeat our parsing. this takes about 5..10 ms)
|
||||
|
||||
// NVidia
|
||||
wdll_ver_Append(L"nvoglv64.dll", versionList);
|
||||
wdll_ver_Append(L"nvoglv32.dll", versionList);
|
||||
wdll_ver_Append(L"nvoglnt.dll", versionList);
|
||||
|
||||
// ATI
|
||||
wdll_ver_Append(L"atioglxx.dll", versionList);
|
||||
|
||||
// Intel
|
||||
wdll_ver_Append(L"ig4icd32.dll", versionList);
|
||||
wdll_ver_Append(L"ig4icd64.dll", versionList);
|
||||
wdll_ver_Append(L"iglicd32.dll", versionList);
|
||||
}
|
||||
|
||||
|
||||
std::wstring wgfx_DriverInfo()
|
||||
{
|
||||
VersionList versionList;
|
||||
if(AppendDriverVersionsFromRegistry(versionList) != INFO::OK) // (fails on Windows 7)
|
||||
AppendDriverVersionsFromKnownFiles(versionList);
|
||||
return versionList;
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/* Copyright (C) 2017 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_WGFX
|
||||
#define INCLUDED_WGFX
|
||||
|
||||
extern std::wstring wgfx_DriverInfo();
|
||||
|
||||
#endif // #ifndef INCLUDED_WGFX
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2022 Wildfire Games.
|
||||
/* Copyright (C) 2023 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@ -280,69 +280,6 @@ static void EnableLowFragmentationHeap()
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Wow64
|
||||
|
||||
// Wow64 'helpfully' redirects all 32-bit apps' accesses of
|
||||
// %windir%\\system32\\drivers to %windir%\\system32\\drivers\\SysWOW64.
|
||||
// that's bad, because the actual drivers are not in the subdirectory. to
|
||||
// work around this, provide for temporarily disabling redirection.
|
||||
|
||||
static WUTIL_FUNC(pIsWow64Process, BOOL, (HANDLE, PBOOL));
|
||||
static WUTIL_FUNC(pWow64DisableWow64FsRedirection, BOOL, (PVOID*));
|
||||
static WUTIL_FUNC(pWow64RevertWow64FsRedirection, BOOL, (PVOID));
|
||||
|
||||
static bool isWow64;
|
||||
|
||||
static void ImportWow64Functions()
|
||||
{
|
||||
WUTIL_IMPORT_KERNEL32(IsWow64Process, pIsWow64Process);
|
||||
WUTIL_IMPORT_KERNEL32(Wow64DisableWow64FsRedirection, pWow64DisableWow64FsRedirection);
|
||||
WUTIL_IMPORT_KERNEL32(Wow64RevertWow64FsRedirection, pWow64RevertWow64FsRedirection);
|
||||
}
|
||||
|
||||
static void DetectWow64()
|
||||
{
|
||||
// function not found => running on 32-bit Windows
|
||||
if(!pIsWow64Process)
|
||||
{
|
||||
isWow64 = false;
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL isWow64Process = FALSE;
|
||||
const BOOL ok = pIsWow64Process(GetCurrentProcess(), &isWow64Process);
|
||||
WARN_IF_FALSE(ok);
|
||||
isWow64 = (isWow64Process == TRUE);
|
||||
}
|
||||
|
||||
bool wutil_IsWow64()
|
||||
{
|
||||
return isWow64;
|
||||
}
|
||||
|
||||
|
||||
WinScopedDisableWow64Redirection::WinScopedDisableWow64Redirection()
|
||||
{
|
||||
// note: don't just check if the function pointers are valid. 32-bit
|
||||
// Vista includes them but isn't running Wow64, so calling the functions
|
||||
// would fail. since we have to check if actually on Wow64, there's no
|
||||
// more need to verify the pointers (their existence is implied).
|
||||
if(!wutil_IsWow64())
|
||||
return;
|
||||
const BOOL ok = pWow64DisableWow64FsRedirection(&m_wasRedirectionEnabled);
|
||||
WARN_IF_FALSE(ok);
|
||||
}
|
||||
|
||||
WinScopedDisableWow64Redirection::~WinScopedDisableWow64Redirection()
|
||||
{
|
||||
if(!wutil_IsWow64())
|
||||
return;
|
||||
const BOOL ok = pWow64RevertWow64FsRedirection(m_wasRedirectionEnabled);
|
||||
WARN_IF_FALSE(ok);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Status wutil_SetPrivilege(const wchar_t* privilege, bool enable)
|
||||
@ -460,9 +397,6 @@ static Status wutil_Init()
|
||||
|
||||
GetDirectories();
|
||||
|
||||
ImportWow64Functions();
|
||||
DetectWow64();
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2022 Wildfire Games.
|
||||
/* Copyright (C) 2023 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@ -163,22 +163,6 @@ extern const OsPath& wutil_RoamingAppdataPath();
|
||||
extern const OsPath& wutil_PersonalPath();
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Wow64
|
||||
|
||||
extern bool wutil_IsWow64();
|
||||
|
||||
class WinScopedDisableWow64Redirection
|
||||
{
|
||||
public:
|
||||
WinScopedDisableWow64Redirection();
|
||||
~WinScopedDisableWow64Redirection();
|
||||
|
||||
private:
|
||||
void* m_wasRedirectionEnabled;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Status wutil_SetPrivilege(const wchar_t* privilege, bool enable);
|
||||
|
@ -34,8 +34,6 @@
|
||||
#include "scriptinterface/ScriptRequest.h"
|
||||
|
||||
#if OS_WIN
|
||||
#include "lib/sysdep/os/win/wgfx.h"
|
||||
|
||||
// We can't include wutil directly because GL headers conflict with Windows
|
||||
// until we use a proper GL loader.
|
||||
extern void* wutil_GetAppHDC();
|
||||
@ -104,25 +102,8 @@ std::string GetVersionImpl()
|
||||
|
||||
std::string GetDriverInformationImpl()
|
||||
{
|
||||
const std::string version = GetVersionImpl();
|
||||
|
||||
std::string driverInfo;
|
||||
#if OS_WIN
|
||||
driverInfo = CStrW(wgfx_DriverInfo()).ToUTF8();
|
||||
if (driverInfo.empty())
|
||||
#endif
|
||||
{
|
||||
if (!version.empty())
|
||||
{
|
||||
// Add "OpenGL" to differentiate this from the real driver version
|
||||
// (returned by platform-specific detect routines).
|
||||
driverInfo = std::string("OpenGL ") + version;
|
||||
}
|
||||
}
|
||||
|
||||
if (driverInfo.empty())
|
||||
return version;
|
||||
return version + " " + driverInfo;
|
||||
// Usually GL_VERSION contains both OpenGL and driver versions.
|
||||
return reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||
}
|
||||
|
||||
std::vector<std::string> GetExtensionsImpl()
|
||||
|
Loading…
Reference in New Issue
Block a user