Skip to content

Commit

Permalink
Fix crashing on startup with AMD GPUs (#949)
Browse files Browse the repository at this point in the history
* Introduce ScopedGLContext to simplify temporary contexts in OSGLContext_win.
* Fix crashing on startup for AMD GPUs.

This change makes sure there is an active context when
GetGPUInfoAMD() is called. While it is not explicitly stated in
https://www.opengl.org/registry/specs/AMD/wgl_gpu_association.txt
that a GL context needs to be current, it is somewhat implied by
the fact that these extension methods are fetched via
wglGetProcAddress(). According to the Windows docs, function
addresses are specific to a context so using them without an
active context seems to be undefined territory.

This should fix #948
  • Loading branch information
acolwell authored Mar 13, 2024
1 parent d03dc06 commit 9679f57
Showing 1 changed file with 104 additions and 104 deletions.
208 changes: 104 additions & 104 deletions Engine/OSGLContext_win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,6 @@ static std::string GetGPUInfoAMDInternal_string(const OSGLContext_wgl_data* wglI

static bool GetGPUInfoAMDInternal_int(const OSGLContext_wgl_data* wglInfo, UINT gpuID, int info, int* value)
{

std::vector<unsigned int> data;
int totalSize = 0;

Expand All @@ -719,6 +718,36 @@ static bool GetGPUInfoAMDInternal_int(const OSGLContext_wgl_data* wglInfo, UINT
return true;
}

namespace {
class ScopedGLContext {
public:
ScopedGLContext(const GLRendererID& gid) {
assert(!OSGLContext_win::threadHasACurrentContext());
try {
_context = std::make_unique<OSGLContext_win>(FramebufferConfig(), GLVersion.major, GLVersion.minor, false, gid, nullptr);
} catch (const std::exception& e) {
std::cerr << "Failed to create GL context : " << e.what() << std::endl;
return;
}

if (!OSGLContext_win::makeContextCurrent(_context.get())) {
_context.reset();
}
}

~ScopedGLContext() {
if (_context) {
OSGLContext_win::makeContextCurrent(nullptr);
}
}

explicit operator bool() const { return (bool)_context; }

private:
std::unique_ptr<OSGLContext_win> _context;
};
} // namespace

void
OSGLContext_win::getGPUInfos(std::list<OpenGLRendererInfo>& renderers)
{
Expand Down Expand Up @@ -749,37 +778,27 @@ OSGLContext_win::getGPUInfos(std::list<OpenGLRendererInfo>& renderers)
for (std::size_t i = 0; i < gpuHandles.size(); ++i) {
OpenGLRendererInfo info;
info.rendererID.rendererHandle = (void*)gpuHandles[i];
{
ScopedGLContext scopedContext(info.rendererID);
if (!scopedContext) {
continue;
}

std::unique_ptr<OSGLContext_win> context;
try {
GLRendererID gid;
gid.rendererHandle = info.rendererID.rendererHandle;
context.reset( new OSGLContext_win(FramebufferConfig(), GLVersion.major, GLVersion.minor, false, gid, 0) );
} catch (const std::exception& e) {
continue;
}

if ( !makeContextCurrent( context.get() ) ) {
continue;
}
try {
OSGLContext::checkOpenGLVersion();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
continue;
}

try {
OSGLContext::checkOpenGLVersion();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
makeContextCurrent(nullptr);
continue;
info.vendorName = std::string( (const char *) glGetString(GL_VENDOR) );
info.rendererName = std::string( (const char *) glGetString(GL_RENDERER) );
info.glVersionString = std::string( (const char *) glGetString(GL_VERSION) );
info.glslVersionString = std::string( (const char*)glGetString (GL_SHADING_LANGUAGE_VERSION) );
info.maxMemBytes = nvx_get_GPU_mem_info();
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &info.maxTextureSize);
renderers.push_back(info);
}

info.vendorName = std::string( (const char *) glGetString(GL_VENDOR) );
info.rendererName = std::string( (const char *) glGetString(GL_RENDERER) );
info.glVersionString = std::string( (const char *) glGetString(GL_VERSION) );
info.glslVersionString = std::string( (const char*)glGetString (GL_SHADING_LANGUAGE_VERSION) );
info.maxMemBytes = nvx_get_GPU_mem_info();
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &info.maxTextureSize);
renderers.push_back(info);

makeContextCurrent(nullptr);
}
} else if (wglInfo->AMD_gpu_association && !isApplication32Bits()) {
//https://www.opengl.org/registry/specs/AMD/wgl_gpu_association.txt
Expand All @@ -798,62 +817,52 @@ OSGLContext_win::getGPUInfos(std::list<OpenGLRendererInfo>& renderers)
UINT gpuID = gpuIDs[index];

OpenGLRendererInfo info;
info.rendererName = GetGPUInfoAMDInternal_string(wglInfo, gpuID, WGL_GPU_RENDERER_STRING_AMD);
if (info.rendererName.empty()) {
continue;
}

info.vendorName = GetGPUInfoAMDInternal_string(wglInfo, gpuID, WGL_GPU_VENDOR_AMD);
if (info.vendorName.empty()) {
continue;
}
info.rendererID.renderID = gpuID;

info.glVersionString = GetGPUInfoAMDInternal_string(wglInfo, gpuID, WGL_GPU_OPENGL_VERSION_STRING_AMD);
if (info.glVersionString.empty()) {
continue;
}
{
ScopedGLContext scopedContext(info.rendererID);
if (!scopedContext) {
continue;
}

// note: cannot retrieve GL_SHADING_LANGUAGE_VERSION
info.rendererName = GetGPUInfoAMDInternal_string(wglInfo, gpuID, WGL_GPU_RENDERER_STRING_AMD);
if (info.rendererName.empty()) {
continue;
}

info.maxMemBytes = 0;
if (!isApplication32Bits()) {
int ramMB = 0;
// AMD drivers are f*** up in 32 bits, they read a wrong buffer size.
// It works fine in 64 bits mode
if (!GetGPUInfoAMDInternal_int(wglInfo, gpuID, WGL_GPU_RAM_AMD, &ramMB)) {
info.vendorName = GetGPUInfoAMDInternal_string(wglInfo, gpuID, WGL_GPU_VENDOR_AMD);
if (info.vendorName.empty()) {
continue;
}
info.maxMemBytes = ramMB * 1e6;
}

info.rendererID.renderID = gpuID;

std::unique_ptr<OSGLContext_win> context;
info.glVersionString = GetGPUInfoAMDInternal_string(wglInfo, gpuID, WGL_GPU_OPENGL_VERSION_STRING_AMD);
if (info.glVersionString.empty()) {
continue;
}

GLRendererID gid;
gid.renderID = info.rendererID.renderID;
try {
context.reset( new OSGLContext_win(FramebufferConfig(), GLVersion.major, GLVersion.minor, false, gid, 0) );
} catch (const std::exception& e) {
continue;
}
// note: cannot retrieve GL_SHADING_LANGUAGE_VERSION

info.maxMemBytes = 0;
if (!isApplication32Bits()) {
int ramMB = 0;
// AMD drivers are f*** up in 32 bits, they read a wrong buffer size.
// It works fine in 64 bits mode
if (!GetGPUInfoAMDInternal_int(wglInfo, gpuID, WGL_GPU_RAM_AMD, &ramMB)) {
continue;
}
info.maxMemBytes = ramMB * 1e6;
}

if ( !makeContextCurrent( context.get() ) ) {
continue;
}
try {
OSGLContext::checkOpenGLVersion();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
continue;
}

try {
OSGLContext::checkOpenGLVersion();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
makeContextCurrent(nullptr);
continue;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &info.maxTextureSize);
renderers.push_back(info);
}

glGetIntegerv(GL_MAX_TEXTURE_SIZE, &info.maxTextureSize);
renderers.push_back(info);

makeContextCurrent(nullptr);
}
}
}
Expand All @@ -863,38 +872,29 @@ OSGLContext_win::getGPUInfos(std::list<OpenGLRendererInfo>& renderers)
}
if (defaultFallback) {
// No extension, use default
std::unique_ptr<OSGLContext_win> context;
try {
context.reset( new OSGLContext_win(FramebufferConfig(), GLVersion.major, GLVersion.minor, false, GLRendererID(), 0) );
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;

return;
}
{
ScopedGLContext scopedContext{GLRendererID()};
if (!scopedContext) {
return;
}

if ( !makeContextCurrent( context.get() ) ) {
return;
}
try {
OSGLContext::checkOpenGLVersion();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return;
}

try {
OSGLContext::checkOpenGLVersion();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
makeContextCurrent(nullptr);
return;
OpenGLRendererInfo info;
info.vendorName = std::string( (const char *) glGetString(GL_VENDOR) );
info.rendererName = std::string( (const char *) glGetString(GL_RENDERER) );
info.glVersionString = std::string( (const char *) glGetString(GL_VERSION) );
info.glslVersionString = std::string( (const char *) glGetString (GL_SHADING_LANGUAGE_VERSION) );
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &info.maxTextureSize);
// We don't have any way to get memory size, set it to 0
info.maxMemBytes = nvx_get_GPU_mem_info();
renderers.push_back(info);
}

OpenGLRendererInfo info;
info.vendorName = std::string( (const char *) glGetString(GL_VENDOR) );
info.rendererName = std::string( (const char *) glGetString(GL_RENDERER) );
info.glVersionString = std::string( (const char *) glGetString(GL_VERSION) );
info.glslVersionString = std::string( (const char *) glGetString (GL_SHADING_LANGUAGE_VERSION) );
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &info.maxTextureSize);
// We don't have any way to get memory size, set it to 0
info.maxMemBytes = nvx_get_GPU_mem_info();
renderers.push_back(info);

makeContextCurrent(nullptr);
}
} // OSGLContext_win::getGPUInfos

Expand Down

0 comments on commit 9679f57

Please sign in to comment.