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:
parent
a2ea86fae0
commit
67e5505899
|
@ -38,6 +38,8 @@ namespace BizHawk.Bizware.Graphics
|
||||||
{
|
{
|
||||||
public EDispMethod DispMethodEnum => EDispMethod.OpenGL;
|
public EDispMethod DispMethodEnum => EDispMethod.OpenGL;
|
||||||
|
|
||||||
|
private static readonly bool _supportsOpenGL3 = OpenGLVersion.SupportsVersion(3, 0);
|
||||||
|
|
||||||
private readonly GL GL;
|
private readonly GL GL;
|
||||||
private readonly ExtFramebufferObject EXT;
|
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
|
// 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(() =>
|
private static readonly Lazy<bool> _available = new(() =>
|
||||||
{
|
{
|
||||||
switch (SDL2OpenGLContext.Version)
|
if (_supportsOpenGL3)
|
||||||
{
|
{
|
||||||
case >= 300:
|
return true;
|
||||||
return true;
|
|
||||||
case < 200:
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
using var gl = GL.GetApi(SDL2OpenGLContext.GetGLProcAddress);
|
||||||
return gl.IsExtensionPresent("EXT_framebuffer_object") || gl.IsExtensionPresent("ARB_framebuffer_object");
|
return gl.IsExtensionPresent("EXT_framebuffer_object") || gl.IsExtensionPresent("ARB_framebuffer_object");
|
||||||
|
@ -77,9 +81,9 @@ namespace BizHawk.Bizware.Graphics
|
||||||
GL = GL.GetApi(SDL2OpenGLContext.GetGLProcAddress);
|
GL = GL.GetApi(SDL2OpenGLContext.GetGLProcAddress);
|
||||||
|
|
||||||
// might need to use EXT if < OpenGL 3.0 and ARB_framebuffer_object is unavailable
|
// 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
|
// ARB_framebuffer_object entrypoints are identical to standard OpenGL 3.0 ones
|
||||||
// EXT_framebuffer_object has differently named entrypoints so needs a separate object
|
// EXT_framebuffer_object has differently named entrypoints so needs a separate object
|
||||||
|
@ -166,7 +170,7 @@ namespace BizHawk.Bizware.Graphics
|
||||||
|
|
||||||
public IGraphicsControl Internal_CreateGraphicsControl()
|
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
|
ret.CreateControl(); // DisplayManager relies on this context being active for creating the GuiRenderer
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace BizHawk.Bizware.Graphics
|
||||||
{
|
{
|
||||||
internal class OpenGLControl : Control, IGraphicsControl
|
internal class OpenGLControl : Control, IGraphicsControl
|
||||||
{
|
{
|
||||||
|
private readonly bool _openGL3;
|
||||||
private readonly Action _contextChangeCallback;
|
private readonly Action _contextChangeCallback;
|
||||||
|
|
||||||
public SDL2OpenGLContext Context { get; private set; }
|
public SDL2OpenGLContext Context { get; private set; }
|
||||||
|
@ -18,8 +19,9 @@ namespace BizHawk.Bizware.Graphics
|
||||||
set => throw new NotImplementedException();
|
set => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenGLControl(Action contextChangeCallback)
|
public OpenGLControl(bool openGL3, Action contextChangeCallback)
|
||||||
{
|
{
|
||||||
|
_openGL3 = openGL3;
|
||||||
_contextChangeCallback = contextChangeCallback;
|
_contextChangeCallback = contextChangeCallback;
|
||||||
|
|
||||||
// according to OpenTK, these are the styles we want to set
|
// 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)
|
protected override void OnHandleCreated(EventArgs e)
|
||||||
{
|
{
|
||||||
base.OnHandleCreated(e);
|
base.OnHandleCreated(e);
|
||||||
Context = new(Handle, 2, 0, false);
|
Context = new(Handle, _openGL3 ? 3 : 2, 0, false, false);
|
||||||
_contextChangeCallback();
|
_contextChangeCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,35 +44,10 @@ namespace BizHawk.Bizware.Graphics
|
||||||
SDL_SetHint(SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP, "0");
|
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 _sdlWindow;
|
||||||
private IntPtr _glContext;
|
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)
|
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()}");
|
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
|
if (SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_FLAGS, forwardCompatible
|
||||||
? (int)SDL_GLcontext.SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG : 0) != 0)
|
? (int)SDL_GLcontext.SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG : 0) != 0)
|
||||||
{
|
{
|
||||||
throw new($"Could not set GL Context Flags! SDL Error: {SDL_GetError()}");
|
throw new($"Could not set GL Context Flags! SDL Error: {SDL_GetError()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're requesting OpenGL 3.3+, get the core profile
|
var profile = coreProfile
|
||||||
// profiles don't exist otherwise
|
|
||||||
var profile = majorVersion * 10 + minorVersion >= 33
|
|
||||||
? SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE
|
? 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)
|
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);
|
_sdlWindow = SDL_CreateWindowFrom(nativeWindowhandle);
|
||||||
if (_sdlWindow == IntPtr.Zero)
|
if (_sdlWindow == IntPtr.Zero)
|
||||||
|
@ -122,10 +96,10 @@ namespace BizHawk.Bizware.Graphics
|
||||||
throw new($"Could not set share context attribute! SDL Error: {SDL_GetError()}");
|
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,
|
_sdlWindow = SDL_CreateWindow(null, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1, 1,
|
||||||
SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN);
|
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)
|
// 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
|
// 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)
|
|
||||||
if (SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1) != 0)
|
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()}");
|
throw new($"Could not set share context attribute! SDL Error: {SDL_GetError()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateContext(majorVersion, minorVersion, forwardCompatible);
|
CreateContext(majorVersion, minorVersion, coreProfile, forwardCompatible);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
@ -10,10 +10,11 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class OpenGLProvider : IOpenGLProvider
|
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)
|
public object RequestGLContext(int major, int minor, bool coreProfile, bool forwardCompatible)
|
||||||
=> new SDL2OpenGLContext(major, minor, forwardCompatible);
|
=> new SDL2OpenGLContext(major, minor, coreProfile, forwardCompatible);
|
||||||
|
|
||||||
public void ReleaseGLContext(object context)
|
public void ReleaseGLContext(object context)
|
||||||
=> ((SDL2OpenGLContext)context).Dispose();
|
=> ((SDL2OpenGLContext)context).Dispose();
|
||||||
|
|
|
@ -235,7 +235,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
}
|
}
|
||||||
var igl = new IGL_OpenGL();
|
var igl = new IGL_OpenGL();
|
||||||
// need to have a context active for checking renderer, will be disposed afterwards
|
// 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);
|
return CheckRenderer(igl);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,19 @@ namespace BizHawk.Emulation.Common
|
||||||
public interface IOpenGLProvider
|
public interface IOpenGLProvider
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public int GLVersion { get; }
|
public bool SupportsGLVersion(int major, int minor);
|
||||||
|
|
||||||
/// <summary>
|
/// <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
|
/// The requested OpenGL context will be shared with the current context
|
||||||
/// Note: creating a context implicitly makes that created context current
|
/// Note: creating a context implicitly makes that created context current
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object RequestGLContext(int major, int minor, bool forwardCompatible);
|
public object RequestGLContext(int major, int minor, bool coreProfile, bool forwardCompatible);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Frees this OpenGL context
|
/// Frees this OpenGL context
|
||||||
|
|
|
@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.N3DS
|
||||||
_configCallbackInterface.GetString = GetStringSettingCallback;
|
_configCallbackInterface.GetString = GetStringSettingCallback;
|
||||||
|
|
||||||
_openGLProvider = lp.Comm.OpenGLProvider;
|
_openGLProvider = lp.Comm.OpenGLProvider;
|
||||||
_supportsOpenGL43 = _openGLProvider.GLVersion >= 430;
|
_supportsOpenGL43 = _openGLProvider.SupportsGLVersion(4, 3);
|
||||||
if (!_supportsOpenGL43/* && _syncSettings.GraphicsApi == CitraSyncSettings.EGraphicsApi.OpenGL*/)
|
if (!_supportsOpenGL43/* && _syncSettings.GraphicsApi == CitraSyncSettings.EGraphicsApi.OpenGL*/)
|
||||||
{
|
{
|
||||||
throw new("OpenGL 4.3 is required, but it is not supported on this machine");
|
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()
|
private IntPtr RequestGLContextCallback()
|
||||||
{
|
{
|
||||||
var context = _openGLProvider.RequestGLContext(4, 3, false);
|
var context = _openGLProvider.RequestGLContext(4, 3, true, false);
|
||||||
_glContexts.Add(context);
|
_glContexts.Add(context);
|
||||||
var handle = GCHandle.Alloc(context, GCHandleType.Weak);
|
var handle = GCHandle.Alloc(context, GCHandleType.Weak);
|
||||||
return GCHandle.ToIntPtr(handle);
|
return GCHandle.ToIntPtr(handle);
|
||||||
|
|
Loading…
Reference in New Issue