Rework OpenGL version support checking to be more robust, slightly rework OpenGL context creation request (they can now explicitly request the core profile)

This commit is contained in:
CasualPokePlayer 2023-07-31 22:32:30 -07:00
parent a2ea86fae0
commit 67e5505899
8 changed files with 105 additions and 57 deletions

View File

@ -38,6 +38,8 @@ namespace BizHawk.Bizware.Graphics
{
public EDispMethod DispMethodEnum => EDispMethod.OpenGL;
private static readonly bool _supportsOpenGL3 = OpenGLVersion.SupportsVersion(3, 0);
private readonly GL GL;
private readonly ExtFramebufferObject EXT;
@ -50,15 +52,17 @@ namespace BizHawk.Bizware.Graphics
// this IGL either requires at least OpenGL 3.0, or OpenGL 2.0 + the EXT_framebuffer_object or ARB_framebuffer_object extension present
private static readonly Lazy<bool> _available = new(() =>
{
switch (SDL2OpenGLContext.Version)
if (_supportsOpenGL3)
{
case >= 300:
return true;
case < 200:
return false;
return true;
}
using (new SDL2OpenGLContext(2, 0, false))
if (!OpenGLVersion.SupportsVersion(2, 0))
{
return false;
}
using (new SDL2OpenGLContext(2, 0, false, false))
{
using var gl = GL.GetApi(SDL2OpenGLContext.GetGLProcAddress);
return gl.IsExtensionPresent("EXT_framebuffer_object") || gl.IsExtensionPresent("ARB_framebuffer_object");
@ -77,9 +81,9 @@ namespace BizHawk.Bizware.Graphics
GL = GL.GetApi(SDL2OpenGLContext.GetGLProcAddress);
// might need to use EXT if < OpenGL 3.0 and ARB_framebuffer_object is unavailable
if (SDL2OpenGLContext.Version < 300)
if (!_supportsOpenGL3)
{
using (new SDL2OpenGLContext(2, 0, false))
using (new SDL2OpenGLContext(2, 0, false, false))
{
// ARB_framebuffer_object entrypoints are identical to standard OpenGL 3.0 ones
// EXT_framebuffer_object has differently named entrypoints so needs a separate object
@ -166,7 +170,7 @@ namespace BizHawk.Bizware.Graphics
public IGraphicsControl Internal_CreateGraphicsControl()
{
var ret = new OpenGLControl(ContextChangeCallback);
var ret = new OpenGLControl(_supportsOpenGL3, ContextChangeCallback);
ret.CreateControl(); // DisplayManager relies on this context being active for creating the GuiRenderer
return ret;
}

View File

@ -8,6 +8,7 @@ namespace BizHawk.Bizware.Graphics
{
internal class OpenGLControl : Control, IGraphicsControl
{
private readonly bool _openGL3;
private readonly Action _contextChangeCallback;
public SDL2OpenGLContext Context { get; private set; }
@ -18,8 +19,9 @@ namespace BizHawk.Bizware.Graphics
set => throw new NotImplementedException();
}
public OpenGLControl(Action contextChangeCallback)
public OpenGLControl(bool openGL3, Action contextChangeCallback)
{
_openGL3 = openGL3;
_contextChangeCallback = contextChangeCallback;
// according to OpenTK, these are the styles we want to set
@ -52,7 +54,7 @@ namespace BizHawk.Bizware.Graphics
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
Context = new(Handle, 2, 0, false);
Context = new(Handle, _openGL3 ? 3 : 2, 0, false, false);
_contextChangeCallback();
}

View File

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using BizHawk.Common.CollectionExtensions;
using Silk.NET.OpenGL.Legacy;
using static SDL2.SDL;
namespace BizHawk.Bizware.Graphics
{
/// <summary>
/// Wraps checking OpenGL versions
/// </summary>
public static class OpenGLVersion
{
// TODO: make this a ref struct once we're c# 10 (parameterless struct ctor)
private class SavedOpenGLContext : IDisposable
{
private readonly IntPtr _sdlWindow, _glContext;
public SavedOpenGLContext()
{
_sdlWindow = SDL_GL_GetCurrentWindow();
_glContext = SDL_GL_GetCurrentContext();
}
public void Dispose()
{
_ = SDL_GL_MakeCurrent(_sdlWindow, _glContext);
}
}
private static readonly IDictionary<int, bool> _glSupport = new Dictionary<int, bool>();
private static int PackGLVersion(int major, int minor)
=> major * 10 + minor;
private static bool CheckVersion(int requestedMajor, int requestedMinor)
{
using (new SavedOpenGLContext())
{
try
{
using (new SDL2OpenGLContext(requestedMajor, requestedMinor, true, false))
{
using var gl = GL.GetApi(SDL2OpenGLContext.GetGLProcAddress);
var versionString = gl.GetStringS(StringName.Version);
var versionParts = versionString!.Split('.');
var major = int.Parse(versionParts[0]);
var minor = int.Parse(versionParts[1][0].ToString());
return PackGLVersion(major, minor) >= PackGLVersion(requestedMajor, requestedMinor);
}
}
catch
{
return false;
}
}
}
public static bool SupportsVersion(int major, int minor)
=> _glSupport.GetValueOrPut(PackGLVersion(major, minor),
static version => CheckVersion(version / 10, version % 10));
}
}

View File

@ -44,35 +44,10 @@ namespace BizHawk.Bizware.Graphics
SDL_SetHint(SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP, "0");
}
private static readonly Lazy<int> _version = new(() =>
{
var prevWindow = SDL_GL_GetCurrentWindow();
var prevContext = SDL_GL_GetCurrentContext();
try
{
using (new SDL2OpenGLContext(2, 0, false))
{
using var gl = GL.GetApi(GetGLProcAddress);
var versionString = gl.GetStringS(StringName.Version);
var versionParts = versionString!.Split('.');
var major = int.Parse(versionParts[0]);
var minor = int.Parse(versionParts[1][0].ToString());
return major * 100 + minor * 10;
}
}
finally
{
SDL_GL_MakeCurrent(prevWindow, prevContext);
}
});
public static int Version => _version.Value;
private IntPtr _sdlWindow;
private IntPtr _glContext;
private void CreateContext(int majorVersion, int minorVersion, bool forwardCompatible)
private void CreateContext(int majorVersion, int minorVersion, bool coreProfile, bool forwardCompatible)
{
if (SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, majorVersion) != 0)
{
@ -84,17 +59,16 @@ namespace BizHawk.Bizware.Graphics
throw new($"Could not set GL Minor Version! SDL Error: {SDL_GetError()}");
}
// TODO: Debug flag / debug callback with DEBUG build
if (SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_FLAGS, forwardCompatible
? (int)SDL_GLcontext.SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG : 0) != 0)
{
throw new($"Could not set GL Context Flags! SDL Error: {SDL_GetError()}");
}
// if we're requesting OpenGL 3.3+, get the core profile
// profiles don't exist otherwise
var profile = majorVersion * 10 + minorVersion >= 33
var profile = coreProfile
? SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE
: 0;
: SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
if (SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, profile) != 0)
{
@ -108,7 +82,7 @@ namespace BizHawk.Bizware.Graphics
}
}
public SDL2OpenGLContext(IntPtr nativeWindowhandle, int majorVersion, int minorVersion, bool forwardCompatible)
public SDL2OpenGLContext(IntPtr nativeWindowhandle, int majorVersion, int minorVersion, bool coreProfile, bool forwardCompatible)
{
_sdlWindow = SDL_CreateWindowFrom(nativeWindowhandle);
if (_sdlWindow == IntPtr.Zero)
@ -122,10 +96,10 @@ namespace BizHawk.Bizware.Graphics
throw new($"Could not set share context attribute! SDL Error: {SDL_GetError()}");
}
CreateContext(majorVersion, minorVersion, forwardCompatible);
CreateContext(majorVersion, minorVersion, coreProfile, forwardCompatible);
}
public SDL2OpenGLContext(int majorVersion, int minorVersion, bool forwardCompatible)
public SDL2OpenGLContext(int majorVersion, int minorVersion, bool coreProfile, bool forwardCompatible)
{
_sdlWindow = SDL_CreateWindow(null, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1, 1,
SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN);
@ -135,14 +109,13 @@ namespace BizHawk.Bizware.Graphics
}
// offscreen contexts are shared (as we want to send texture from it over to our control's context)
// make sure to set the current graphics' control context before creating this context
// (if no context is set, i.e. first IGL, then this won't do anything)
// make sure to set the current graphics control context before creating this context
if (SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1) != 0)
{
throw new($"Could not set share context attribute! SDL Error: {SDL_GetError()}");
}
CreateContext(majorVersion, minorVersion, forwardCompatible);
CreateContext(majorVersion, minorVersion, coreProfile, forwardCompatible);
}
public void Dispose()

View File

@ -10,10 +10,11 @@ namespace BizHawk.Client.EmuHawk
/// </summary>
public class OpenGLProvider : IOpenGLProvider
{
public int GLVersion => SDL2OpenGLContext.Version;
public bool SupportsGLVersion(int major, int minor)
=> OpenGLVersion.SupportsVersion(major, minor);
public object RequestGLContext(int major, int minor, bool forwardCompatible)
=> new SDL2OpenGLContext(major, minor, forwardCompatible);
public object RequestGLContext(int major, int minor, bool coreProfile, bool forwardCompatible)
=> new SDL2OpenGLContext(major, minor, coreProfile, forwardCompatible);
public void ReleaseGLContext(object context)
=> ((SDL2OpenGLContext)context).Dispose();

View File

@ -235,7 +235,7 @@ namespace BizHawk.Client.EmuHawk
}
var igl = new IGL_OpenGL();
// need to have a context active for checking renderer, will be disposed afterwards
using (new SDL2OpenGLContext(2, 0, false))
using (new SDL2OpenGLContext(OpenGLVersion.SupportsVersion(3, 0) ? 3 : 2, 0, false, false))
{
return CheckRenderer(igl);
}

View File

@ -8,16 +8,19 @@ namespace BizHawk.Emulation.Common
public interface IOpenGLProvider
{
/// <summary>
/// OpenGL version, using major.minor.build format with decimal points ommitted (e.g. 1.2.3 turns into 123)
/// Checks if specified OpenGL version is supported
/// The current context will be preserved
/// </summary>
public int GLVersion { get; }
public bool SupportsGLVersion(int major, int minor);
/// <summary>
/// Requests an OpenGL context with specified major / minor / forwardCompatible
/// Requests an OpenGL context with specified major / minor
/// The core profile can be requested (otherwise, the compatibility profile will be used)
/// The forward compatible bit can also be requested
/// The requested OpenGL context will be shared with the current context
/// Note: creating a context implicitly makes that created context current
/// </summary>
public object RequestGLContext(int major, int minor, bool forwardCompatible);
public object RequestGLContext(int major, int minor, bool coreProfile, bool forwardCompatible);
/// <summary>
/// Frees this OpenGL context

View File

@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.N3DS
_configCallbackInterface.GetString = GetStringSettingCallback;
_openGLProvider = lp.Comm.OpenGLProvider;
_supportsOpenGL43 = _openGLProvider.GLVersion >= 430;
_supportsOpenGL43 = _openGLProvider.SupportsGLVersion(4, 3);
if (!_supportsOpenGL43/* && _syncSettings.GraphicsApi == CitraSyncSettings.EGraphicsApi.OpenGL*/)
{
throw new("OpenGL 4.3 is required, but it is not supported on this machine");
@ -161,7 +161,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.N3DS
private IntPtr RequestGLContextCallback()
{
var context = _openGLProvider.RequestGLContext(4, 3, false);
var context = _openGLProvider.RequestGLContext(4, 3, true, false);
_glContexts.Add(context);
var handle = GCHandle.Alloc(context, GCHandleType.Weak);
return GCHandle.ToIntPtr(handle);