A game framework written with osu! in mind.
at master 132 lines 5.1 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System; 5using System.Diagnostics; 6using System.Linq; 7using System.Reflection; 8using System.Runtime.InteropServices; 9using osu.Framework.Graphics.OpenGL; 10using osu.Framework.Logging; 11using osuTK.Graphics; 12using osuTK.Graphics.ES30; 13 14namespace osu.Framework.Platform 15{ 16 /// <summary> 17 /// Implementation of <see cref="IGraphicsBackend"/> that force-loads OpenGL 18 /// endpoints into osuTK's bindings. 19 /// </summary> 20 public abstract class PassthroughGraphicsBackend : IGraphicsBackend 21 { 22 internal IntPtr Context; 23 24 internal Version GLVersion { get; private set; } 25 26 internal Version GLSLVersion { get; private set; } 27 28 internal bool IsEmbedded { get; private set; } 29 30 public abstract bool VerticalSync { get; set; } 31 32 protected abstract IntPtr CreateContext(); 33 protected abstract void MakeCurrent(IntPtr context); 34 protected abstract IntPtr GetProcAddress(string symbol); 35 36 public abstract void SwapBuffers(); 37 38 public virtual void Initialise(IWindow window) 39 { 40 Context = CreateContext(); 41 42 MakeCurrent(Context); 43 44 loadTKBindings(); 45 46 string version = GL.GetString(StringName.Version); 47 string versionNumberSubstring = getVersionNumberSubstring(version); 48 49 GLVersion = new Version(versionNumberSubstring); 50 51 // As defined by https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glGetString.xml 52 IsEmbedded = version.Contains("OpenGL ES"); 53 GLWrapper.IsEmbedded = IsEmbedded; 54 55 version = GL.GetString(StringName.ShadingLanguageVersion); 56 57 if (!string.IsNullOrEmpty(version)) 58 { 59 try 60 { 61 GLSLVersion = new Version(versionNumberSubstring); 62 } 63 catch (Exception e) 64 { 65 Logger.Error(e, $@"couldn't set GLSL version using string '{version}'"); 66 } 67 } 68 69 if (GLSLVersion == null) 70 GLSLVersion = new Version(); 71 72 Logger.Log($@"GL Initialized 73 GL Version: {GL.GetString(StringName.Version)} 74 GL Renderer: {GL.GetString(StringName.Renderer)} 75 GL Shader Language version: {GL.GetString(StringName.ShadingLanguageVersion)} 76 GL Vendor: {GL.GetString(StringName.Vendor)} 77 GL Extensions: {GL.GetString(StringName.Extensions)}"); 78 79 // We need to release the context in this thread, since Windows locks it and prevents 80 // the draw thread from taking it. macOS seems to gracefully ignore this. 81 MakeCurrent(IntPtr.Zero); 82 } 83 84 public void MakeCurrent() => MakeCurrent(Context); 85 86 public void ClearCurrent() => MakeCurrent(IntPtr.Zero); 87 88 private void loadTKBindings() 89 { 90 loadEntryPoints(new osuTK.Graphics.OpenGL.GL()); 91 loadEntryPoints(new osuTK.Graphics.OpenGL4.GL()); 92 loadEntryPoints(new osuTK.Graphics.ES11.GL()); 93 loadEntryPoints(new osuTK.Graphics.ES20.GL()); 94 loadEntryPoints(new GL()); 95 } 96 97 private unsafe void loadEntryPoints(GraphicsBindingsBase bindings) 98 { 99 var type = bindings.GetType(); 100 var pointsInfo = type.GetRuntimeFields().First(x => x.Name == "_EntryPointsInstance"); 101 var namesInfo = type.GetRuntimeFields().First(x => x.Name == "_EntryPointNamesInstance"); 102 var offsetsInfo = type.GetRuntimeFields().First(x => x.Name == "_EntryPointNameOffsetsInstance"); 103 104 var entryPointsInstance = (IntPtr[])pointsInfo.GetValue(bindings); 105 var entryPointNamesInstance = (byte[])namesInfo.GetValue(bindings); 106 var entryPointNameOffsetsInstance = (int[])offsetsInfo.GetValue(bindings); 107 108 Debug.Assert(entryPointsInstance != null); 109 Debug.Assert(entryPointNameOffsetsInstance != null); 110 111 fixed (byte* name = entryPointNamesInstance) 112 { 113 for (int i = 0; i < entryPointsInstance.Length; i++) 114 { 115 var ptr = name + entryPointNameOffsetsInstance[i]; 116 var str = Marshal.PtrToStringAnsi(new IntPtr(ptr)); 117 entryPointsInstance[i] = GetProcAddress(str); 118 } 119 } 120 121 pointsInfo.SetValue(bindings, entryPointsInstance); 122 } 123 124 private string getVersionNumberSubstring(string version) 125 { 126 string result = version.Split(' ').FirstOrDefault(s => char.IsDigit(s, 0)); 127 if (result != null) return result; 128 129 throw new ArgumentException($"Invalid version string: \"{version}\"", nameof(version)); 130 } 131 } 132}