diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 4c2769bcc4..d4c4b64058 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -1,6 +1,7 @@  - netstandard2.0 + netstandard2.0 + true @@ -17,6 +18,7 @@ + diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 874560a294..9749c1c4cf 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -14,6 +14,7 @@ + diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml b/samples/ControlCatalog/Pages/OpenGlPage.xaml new file mode 100644 index 0000000000..30f8f76240 --- /dev/null +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml @@ -0,0 +1,18 @@ + + + + + + Yaw + + Pitch + + Roll + + + + + diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs new file mode 100644 index 0000000000..791c49ed8f --- /dev/null +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs @@ -0,0 +1,288 @@ +using System; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Runtime.InteropServices; +using Avalonia; +using Avalonia.Controls; +using Avalonia.OpenGL; +using static Avalonia.OpenGL.GlConsts; +// ReSharper disable StringLiteralTypo + +namespace ControlCatalog.Pages +{ + public class OpenGlPage : UserControl + { + + } + + public class OpenGlPageControl : OpenGlControlBase + { + private float _yaw; + + public static readonly DirectProperty YawProperty = + AvaloniaProperty.RegisterDirect("Yaw", o => o.Yaw, (o, v) => o.Yaw = v); + + public float Yaw + { + get => _yaw; + set => SetAndRaise(YawProperty, ref _yaw, value); + } + + private float _pitch; + + public static readonly DirectProperty PitchProperty = + AvaloniaProperty.RegisterDirect("Pitch", o => o.Pitch, (o, v) => o.Pitch = v); + + public float Pitch + { + get => _pitch; + set => SetAndRaise(PitchProperty, ref _pitch, value); + } + + + private float _roll; + + public static readonly DirectProperty RollProperty = + AvaloniaProperty.RegisterDirect("Roll", o => o.Roll, (o, v) => o.Roll = v); + + public float Roll + { + get => _roll; + set => SetAndRaise(RollProperty, ref _roll, value); + } + + static OpenGlPageControl() + { + AffectsRender(YawProperty, PitchProperty, RollProperty); + } + + private int _vertexShader; + private int _fragmentShader; + private int _shaderProgram; + private int _vertexBufferObject; + private int _indexBufferObject; + private int _vertexArrayObject; + + private string WithVersion(string shader) => + $"#version {(DisplayType == GlDisplayType.OpenGl ? 120 : 100)}\n" + shader; + + private string WithVersionAndPrecision(string shader, string precision) => + WithVersion(DisplayType == GlDisplayType.OpenGLES ? $"precision {precision}\n{shader}" : shader); + + private string VertexShaderSource => WithVersion(@" + attribute vec3 aPos; + attribute vec3 aNormal; + uniform mat4 uModel; + uniform mat4 uProjection; + uniform mat4 uView; + + varying vec3 FragPos; + varying vec3 VecPos; + varying vec3 Normal; + void main() + { + gl_Position = uProjection * uView * uModel * vec4(aPos, 1.0); + FragPos = vec3(uModel * vec4(aPos, 1.0)); + VecPos = aPos; + Normal = normalize(vec3(uModel * vec4(aNormal, 1.0))); + } +"); + + private string FragmentShaderSource => WithVersionAndPrecision(@" + varying vec3 FragPos; + varying vec3 VecPos; + varying vec3 Normal; + uniform float uMaxY; + uniform float uMinY; + void main() + { + float y = (VecPos.y - uMinY) / (uMaxY - uMinY); + vec3 objectColor = vec3((1 - y), 0.40 + y / 4, y * 0.75+0.25); + //vec3 objectColor = normalize(FragPos); + + + float ambientStrength = 0.3; + vec3 lightColor = vec3(1.0, 1.0, 1.0); + vec3 lightPos = vec3(uMaxY * 2, uMaxY * 2, uMaxY * 2); + vec3 ambient = ambientStrength * lightColor; + + + vec3 norm = normalize(Normal); + vec3 lightDir = normalize(lightPos - FragPos); + + float diff = max(dot(norm, lightDir), 0.0); + vec3 diffuse = diff * lightColor; + + vec3 result = (ambient + diffuse) * objectColor; + gl_FragColor = vec4(result, 1.0); + + } +", "mediump float"); + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct Vertex + { + public Vector3 Position; + public Vector3 Normal; + } + + private readonly Vertex[] _points; + private readonly ushort[] _indices; + private readonly float _minY; + private readonly float _maxY; + + + public OpenGlPageControl() + { + var name = typeof(OpenGlPage).Assembly.GetManifestResourceNames().First(x => x.Contains("teapot.bin")); + using (var sr = new BinaryReader(typeof(OpenGlPage).Assembly.GetManifestResourceStream(name))) + { + var buf = new byte[sr.ReadInt32()]; + sr.Read(buf, 0, buf.Length); + var points = new float[buf.Length / 4]; + Buffer.BlockCopy(buf, 0, points, 0, buf.Length); + buf = new byte[sr.ReadInt32()]; + sr.Read(buf, 0, buf.Length); + _indices = new ushort[buf.Length / 2]; + Buffer.BlockCopy(buf, 0, _indices, 0, buf.Length); + _points = new Vertex[points.Length / 3]; + for (var primitive = 0; primitive < points.Length / 3; primitive++) + { + var srci = primitive * 3; + _points[primitive] = new Vertex + { + Position = new Vector3(points[srci], points[srci + 1], points[srci + 2]) + }; + } + + for (int i = 0; i < _indices.Length; i += 3) + { + Vector3 a = _points[_indices[i]].Position; + Vector3 b = _points[_indices[i + 1]].Position; + Vector3 c = _points[_indices[i + 2]].Position; + var normal = Vector3.Normalize(Vector3.Cross(c - b, a - b)); + + _points[_indices[i]].Normal += normal; + _points[_indices[i + 1]].Normal += normal; + _points[_indices[i + 2]].Normal += normal; + } + + for (int i = 0; i < _points.Length; i++) + { + _points[i].Normal = Vector3.Normalize(_points[i].Normal); + _maxY = Math.Max(_maxY, _points[i].Position.Y); + _minY = Math.Min(_minY, _points[i].Position.Y); + } + } + + } + + private void CheckError(GlInterface gl) + { + int err; + while ((err = gl.GetError()) != GL_NO_ERROR) + Console.WriteLine(err); + } + + protected unsafe override void OnOpenGlInit(GlInterface GL, int fb) + { + // Load the source of the vertex shader and compile it. + _vertexShader = GL.CreateShader(GL_VERTEX_SHADER); + Console.WriteLine(GL.CompileShaderAndGetError(_vertexShader, VertexShaderSource)); + + // Load the source of the fragment shader and compile it. + _fragmentShader = GL.CreateShader(GL_FRAGMENT_SHADER); + Console.WriteLine(GL.CompileShaderAndGetError(_fragmentShader, FragmentShaderSource)); + + // Create the shader program, attach the vertex and fragment shaders and link the program. + _shaderProgram = GL.CreateProgram(); + GL.AttachShader(_shaderProgram, _vertexShader); + GL.AttachShader(_shaderProgram, _fragmentShader); + const int positionLocation = 0; + const int normalLocation = 1; + GL.BindAttribLocationString(_shaderProgram, positionLocation, "aPos"); + GL.BindAttribLocationString(_shaderProgram, normalLocation, "aNormal"); + Console.WriteLine(GL.LinkProgramAndGetError(_shaderProgram)); + CheckError(GL); + + // Create the vertex buffer object (VBO) for the vertex data. + _vertexBufferObject = GL.GenBuffer(); + // Bind the VBO and copy the vertex data into it. + GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject); + var vertexSize = Marshal.SizeOf(); + fixed (void* pdata = _points) + GL.BufferData(GL_ARRAY_BUFFER, new IntPtr(_points.Length * vertexSize), + new IntPtr(pdata), GL_STATIC_DRAW); + + _indexBufferObject = GL.GenBuffer(); + GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject); + fixed (void* pdata = _indices) + GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(_indices.Length * sizeof(ushort)), new IntPtr(pdata), + GL_STATIC_DRAW); + + _vertexArrayObject = GL.GenVertexArray(); + GL.BindVertexArray(_vertexArrayObject); + + GL.VertexAttribPointer(positionLocation, 3, GL_FLOAT, + 0, vertexSize, IntPtr.Zero); + GL.VertexAttribPointer(normalLocation, 3, GL_FLOAT, + 0, vertexSize, new IntPtr(12)); + GL.EnableVertexAttribArray(positionLocation); + GL.EnableVertexAttribArray(normalLocation); + CheckError(GL); + + } + + protected override void OnOpenGlDeinit(GlInterface GL, int fb) + { + // Unbind everything + GL.BindBuffer(GL_ARRAY_BUFFER, 0); + GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + GL.BindVertexArray(0); + GL.UseProgram(0); + + // Delete all resources. + GL.DeleteBuffer(_vertexBufferObject); + GL.DeleteBuffer(_indexBufferObject); + GL.DeleteVertexArray(_vertexArrayObject); + GL.DeleteProgram(_shaderProgram); + GL.DeleteShader(_fragmentShader); + GL.DeleteShader(_vertexShader); + } + + protected override unsafe void OnOpenGlRender(GlInterface gl, int fb) + { + gl.ClearColor(0, 0, 0, 0); + gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + gl.Enable(GL_DEPTH_TEST); + gl.Viewport(0, 0, (int)Bounds.Width, (int)Bounds.Height); + var GL = gl; + + GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject); + GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject); + GL.BindVertexArray(_vertexArrayObject); + GL.UseProgram(_shaderProgram); + var projection = + Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)(Bounds.Width / Bounds.Height), + 0.01f, 1000); + + + var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, -1, 0)); + var model = Matrix4x4.CreateFromYawPitchRoll(_yaw, _pitch, _roll); + var modelLoc = GL.GetUniformLocationString(_shaderProgram, "uModel"); + var viewLoc = GL.GetUniformLocationString(_shaderProgram, "uView"); + var projectionLoc = GL.GetUniformLocationString(_shaderProgram, "uProjection"); + var maxYLoc = GL.GetUniformLocationString(_shaderProgram, "uMaxY"); + var minYLoc = GL.GetUniformLocationString(_shaderProgram, "uMinY"); + GL.UniformMatrix4fv(modelLoc, 1, false, &model); + GL.UniformMatrix4fv(viewLoc, 1, false, &view); + GL.UniformMatrix4fv(projectionLoc, 1, false, &projection); + GL.Uniform1f(maxYLoc, _maxY); + GL.Uniform1f(minYLoc, _minY); + GL.DrawElements(GL_TRIANGLES, _indices.Length, GL_UNSIGNED_SHORT, IntPtr.Zero); + + CheckError(GL); + } + } +} diff --git a/samples/ControlCatalog/Pages/teapot.bin b/samples/ControlCatalog/Pages/teapot.bin new file mode 100644 index 0000000000..589eeb912d Binary files /dev/null and b/samples/ControlCatalog/Pages/teapot.bin differ diff --git a/src/Avalonia.Native/GlPlatformFeature.cs b/src/Avalonia.Native/GlPlatformFeature.cs index e2231dae65..7ed6eaa266 100644 --- a/src/Avalonia.Native/GlPlatformFeature.cs +++ b/src/Avalonia.Native/GlPlatformFeature.cs @@ -49,7 +49,7 @@ namespace Avalonia.Native }); } - public GlDisplayType Type => GlDisplayType.OpenGL2; + public GlDisplayType Type => GlDisplayType.OpenGl; public GlInterface GlInterface { get; } diff --git a/src/Avalonia.OpenGL/EglDisplay.cs b/src/Avalonia.OpenGL/EglDisplay.cs index 57787243b2..f93bf7280c 100644 --- a/src/Avalonia.OpenGL/EglDisplay.cs +++ b/src/Avalonia.OpenGL/EglDisplay.cs @@ -81,7 +81,7 @@ namespace Avalonia.OpenGL Attributes = new[] {EGL_NONE}, Api = EGL_OPENGL_API, RenderableTypeBit = EGL_OPENGL_BIT, - Type = GlDisplayType.OpenGL2 + Type = GlDisplayType.OpenGl }, new { @@ -92,7 +92,7 @@ namespace Avalonia.OpenGL }, Api = EGL_OPENGL_ES_API, RenderableTypeBit = EGL_OPENGL_ES2_BIT, - Type = GlDisplayType.OpenGLES2 + Type = GlDisplayType.OpenGLES } }) { diff --git a/src/Avalonia.OpenGL/GlDisplayType.cs b/src/Avalonia.OpenGL/GlDisplayType.cs index 2e5178bc37..6ce7600ec6 100644 --- a/src/Avalonia.OpenGL/GlDisplayType.cs +++ b/src/Avalonia.OpenGL/GlDisplayType.cs @@ -2,7 +2,7 @@ namespace Avalonia.OpenGL { public enum GlDisplayType { - OpenGL2, - OpenGLES2 + OpenGl, + OpenGLES } -} \ No newline at end of file +} diff --git a/src/Avalonia.OpenGL/GlEntryPointAttribute.cs b/src/Avalonia.OpenGL/GlEntryPointAttribute.cs index 016c3d0af9..62bf3567e4 100644 --- a/src/Avalonia.OpenGL/GlEntryPointAttribute.cs +++ b/src/Avalonia.OpenGL/GlEntryPointAttribute.cs @@ -5,13 +5,18 @@ namespace Avalonia.OpenGL [AttributeUsage(AttributeTargets.Property)] public class GlEntryPointAttribute : Attribute { - public string EntryPoint { get; } + public string[] EntryPoints { get; } public bool Optional { get; } public GlEntryPointAttribute(string entryPoint, bool optional = false) { - EntryPoint = entryPoint; + EntryPoints = new []{entryPoint}; Optional = optional; } + + public GlEntryPointAttribute(params string[] entryPoints) + { + EntryPoints = entryPoints; + } } } diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs index 5614ddfb17..33208f8e6c 100644 --- a/src/Avalonia.OpenGL/GlInterface.cs +++ b/src/Avalonia.OpenGL/GlInterface.cs @@ -1,6 +1,8 @@ using System; using System.Runtime.InteropServices; +using System.Text; using Avalonia.Platform.Interop; +using static Avalonia.OpenGL.GlConsts; namespace Avalonia.OpenGL { @@ -74,6 +76,10 @@ namespace Avalonia.OpenGL [GlEntryPoint("glGenFramebuffers")] public GlGenFramebuffers GenFramebuffers { get; } + public delegate void GlDeleteFramebuffers(int count, int[] framebuffers); + [GlEntryPoint("glDeleteFramebuffers")] + public GlDeleteFramebuffers DeleteFramebuffers { get; } + public delegate void GlBindFramebuffer(int target, int fb); [GlEntryPoint("glBindFramebuffer")] public GlBindFramebuffer BindFramebuffer { get; } @@ -106,6 +112,11 @@ namespace Avalonia.OpenGL public delegate void GlBindTexture(int target, int fb); [GlEntryPoint("glBindTexture")] public GlBindTexture BindTexture { get; } + + public delegate void GlDeleteTextures(int count, int[] textures); + [GlEntryPoint("glDeleteTextures")] + public GlDeleteTextures DeleteTextures { get; } + public delegate void GlTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, IntPtr data); @@ -125,6 +136,207 @@ namespace Avalonia.OpenGL [GlEntryPoint("glDrawBuffers")] public GlDrawBuffers DrawBuffers { get; } + public delegate int GlCreateShader(int shaderType); + [GlEntryPoint("glCreateShader")] + public GlCreateShader CreateShader { get; } + + public delegate void GlShaderSource(int shader, int count, IntPtr strings, IntPtr lengths); + [GlEntryPoint("glShaderSource")] + public GlShaderSource ShaderSource { get; } + + public void ShaderSourceString(int shader, string source) + { + using (var b = new Utf8Buffer(source)) + { + var ptr = b.DangerousGetHandle(); + var len = new IntPtr(b.ByteLen); + ShaderSource(shader, 1, new IntPtr(&ptr), new IntPtr(&len)); + } + } + + public delegate void GlCompileShader(int shader); + [GlEntryPoint("glCompileShader")] + public GlCompileShader CompileShader { get; } + + public delegate void GlGetShaderiv(int shader, int name, int* parameters); + [GlEntryPoint("glGetShaderiv")] + public GlGetShaderiv GetShaderiv { get; } + + public delegate void GlGetShaderInfoLog(int shader, int maxLength, out int length, void*infoLog); + [GlEntryPoint("glGetShaderInfoLog")] + public GlGetShaderInfoLog GetShaderInfoLog { get; } + + public unsafe string CompileShaderAndGetError(int shader, string source) + { + ShaderSourceString(shader, source); + CompileShader(shader); + int compiled; + GetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (compiled != 0) + return null; + int logLength; + GetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength); + if (logLength == 0) + logLength = 4096; + var logData = new byte[logLength]; + int len; + fixed (void* ptr = logData) + GetShaderInfoLog(shader, logLength, out len, ptr); + return Encoding.UTF8.GetString(logData,0, len); + } + + public delegate int GlCreateProgram(); + [GlEntryPoint("glCreateProgram")] + public GlCreateProgram CreateProgram { get; } + + public delegate void GlAttachShader(int program, int shader); + [GlEntryPoint("glAttachShader")] + public GlAttachShader AttachShader { get; } + + public delegate void GlLinkProgram(int program); + [GlEntryPoint("glLinkProgram")] + public GlLinkProgram LinkProgram { get; } + + public delegate void GlGetProgramiv(int program, int name, int* parameters); + [GlEntryPoint("glGetProgramiv")] + public GlGetProgramiv GetProgramiv { get; } + + public delegate void GlGetProgramInfoLog(int program, int maxLength, out int len, void* infoLog); + [GlEntryPoint("glGetProgramInfoLog")] + public GlGetProgramInfoLog GetProgramInfoLog { get; } + + public unsafe string LinkProgramAndGetError(int program) + { + LinkProgram(program); + int compiled; + GetProgramiv(program, GL_LINK_STATUS, &compiled); + if (compiled != 0) + return null; + int logLength; + GetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); + var logData = new byte[logLength]; + int len; + fixed (void* ptr = logData) + GetProgramInfoLog(program, logLength, out len, ptr); + return Encoding.UTF8.GetString(logData,0, len); + } + + public delegate void GlBindAttribLocation(int program, int index, IntPtr name); + [GlEntryPoint("glBindAttribLocation")] + public GlBindAttribLocation BindAttribLocation { get; } + + public void BindAttribLocationString(int program, int index, string name) + { + using (var b = new Utf8Buffer(name)) + BindAttribLocation(program, index, b.DangerousGetHandle()); + } + + public delegate void GlGenBuffers(int len, int[] rv); + [GlEntryPoint("glGenBuffers")] + public GlGenBuffers GenBuffers { get; } + + public int GenBuffer() + { + var rv = new int[1]; + GenBuffers(1, rv); + return rv[0]; + } + + public delegate void GlBindBuffer(int target, int buffer); + [GlEntryPoint("glBindBuffer")] + public GlBindBuffer BindBuffer { get; } + + public delegate void GlBufferData(int target, IntPtr size, IntPtr data, int usage); + [GlEntryPoint("glBufferData")] + public GlBufferData BufferData { get; } + + public delegate int GlGetAttribLocation(int program, IntPtr name); + [GlEntryPoint("glGetAttribLocation")] + public GlGetAttribLocation GetAttribLocation { get; } + + public int GetAttribLocationString(int program, string name) + { + using (var b = new Utf8Buffer(name)) + return GetAttribLocation(program, b.DangerousGetHandle()); + } + + public delegate void GlVertexAttribPointer(int index, int size, int type, + int normalized, int stride, IntPtr pointer); + [GlEntryPoint("glVertexAttribPointer")] + public GlVertexAttribPointer VertexAttribPointer { get; } + + public delegate void GlEnableVertexAttribArray(int index); + [GlEntryPoint("glEnableVertexAttribArray")] + public GlEnableVertexAttribArray EnableVertexAttribArray { get; } + + public delegate void GlUseProgram(int program); + [GlEntryPoint("glUseProgram")] + public GlUseProgram UseProgram { get; } + + public delegate void GlDrawArrays(int mode, int first, IntPtr count); + [GlEntryPoint("glDrawArrays")] + public GlDrawArrays DrawArrays { get; } + + public delegate void GlDrawElements(int mode, int count, int type, IntPtr indices); + [GlEntryPoint("glDrawElements")] + public GlDrawElements DrawElements { get; } + + + public delegate void GlGenVertexArrays(int n, int[] rv); + [GlEntryPoint("glGenVertexArrays", "glGenVertexArraysOES")] + public GlGenVertexArrays GenVertexArrays { get; } + + public int GenVertexArray() + { + var rv = new int[1]; + GenVertexArrays(1, rv); + return rv[0]; + } + + public delegate void GlBindVertexArray(int array); + [GlEntryPoint("glBindVertexArray", "glBindVertexArrayOES")] + public GlBindVertexArray BindVertexArray { get; } + + public delegate int GlGetUniformLocation(int program, IntPtr name); + [GlEntryPoint("glGetUniformLocation")] + public GlGetUniformLocation GetUniformLocation { get; } + + public int GetUniformLocationString(int program, string name) + { + using (var b = new Utf8Buffer(name)) + return GetUniformLocation(program, b.DangerousGetHandle()); + } + + public delegate void GlUniform1f(int location, float falue); + [GlEntryPoint("glUniform1f")] + public GlUniform1f Uniform1f { get; } + + + public delegate void GlUniformMatrix4fv(int location, int count, bool transpose, void* value); + [GlEntryPoint("glUniformMatrix4fv")] + public GlUniformMatrix4fv UniformMatrix4fv { get; } + + public delegate void GlEnable(int what); + [GlEntryPoint("glEnable")] + public GlEnable Enable { get; } + + public delegate void GlDeleteBuffer(int buffer); + [GlEntryPoint("glDeleteBuffer")] + public GlDeleteBuffer DeleteBuffer { get; } + + public delegate void GlDeleteVertexArray(int array); + [GlEntryPoint("glDeleteVertexArray", "glDeleteVertexArrayOES")] + public GlDeleteVertexArray DeleteVertexArray { get; } + + public delegate void GlDeleteProgram(int program); + [GlEntryPoint("glDeleteProgram")] + public GlDeleteProgram DeleteProgram { get; } + + public delegate void GlDeleteShader(int shader); + [GlEntryPoint("glDeleteShader")] + public GlDeleteShader DeleteShader { get; } + + // ReSharper restore UnassignedGetOnlyAutoProperty } } diff --git a/src/Avalonia.OpenGL/GlInterfaceBase.cs b/src/Avalonia.OpenGL/GlInterfaceBase.cs index 89ec0e77d4..445777b065 100644 --- a/src/Avalonia.OpenGL/GlInterfaceBase.cs +++ b/src/Avalonia.OpenGL/GlInterfaceBase.cs @@ -22,7 +22,22 @@ namespace Avalonia.OpenGL BindingFlags.Instance | BindingFlags.NonPublic); if (field == null) throw new InvalidProgramException($"Expected property {prop.Name} to have {fieldName}"); - var proc = getProcAddress(a.EntryPoint, a.Optional); + IntPtr proc = IntPtr.Zero; + if (a.EntryPoints.Length == 1) + proc = getProcAddress(a.EntryPoints[0], a.Optional); + else + { + foreach (var ep in a.EntryPoints) + { + proc = getProcAddress(ep, true); + + } + + if (proc == IntPtr.Zero && !a.Optional) + throw new OpenGlException("Unable to resolve function by any of aliases " + + string.Join(", ", a.EntryPoints)); + } + if (proc != IntPtr.Zero) field.SetValue(this, Marshal.GetDelegateForFunctionPointer(proc, prop.PropertyType)); } diff --git a/src/Avalonia.OpenGL/OpenGlControlBase.cs b/src/Avalonia.OpenGL/OpenGlControlBase.cs new file mode 100644 index 0000000000..13a60c7574 --- /dev/null +++ b/src/Avalonia.OpenGL/OpenGlControlBase.cs @@ -0,0 +1,149 @@ +using System; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.OpenGL.Imaging; +using static Avalonia.OpenGL.GlConsts; + +namespace Avalonia.OpenGL +{ + public abstract class OpenGlControlBase : Control + { + private IGlContext _context; + private int _fb, _texture, _renderBuffer; + private OpenGlTextureBitmap _bitmap; + private Size _oldSize; + protected GlDisplayType DisplayType { get; private set; } + public sealed override void Render(DrawingContext context) + { + if(!EnsureInitialized()) + return; + using (_context.MakeCurrent()) + { + using (_bitmap.Lock()) + { + var gl = _context.Display.GlInterface; + gl.BindFramebuffer(GL_FRAMEBUFFER, _fb); + if (_oldSize != Bounds.Size) + ResizeTexture(gl); + + OnOpenGlRender(gl, _fb); + gl.Flush(); + } + } + + context.DrawImage(_bitmap, 1, new Rect(_bitmap.Size), Bounds); + base.Render(context); + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + if (_context != null) + { + using (_context.MakeCurrent()) + { + OnOpenGlDeinit(_context.Display.GlInterface, _fb); + var gl = _context.Display.GlInterface; + gl.BindTexture(GL_TEXTURE_2D, 0); + gl.BindFramebuffer(GL_FRAMEBUFFER, 0); + gl.DeleteFramebuffers(1, new[] { _fb }); + using (_bitmap.Lock()) + { + _bitmap.SetTexture(0, 0, new PixelSize(1, 1), 1); + gl.DeleteTextures(1, new[] { _texture }); + } + _bitmap.Dispose(); + + _context.Dispose(); + _context = null; + } + } + base.OnDetachedFromVisualTree(e); + } + + bool EnsureInitialized() + { + if (_context != null) + return true; + + var feature = AvaloniaLocator.Current.GetService(); + if (feature == null) + return false; + _context = feature.CreateContext(); + DisplayType = feature.Display.Type; + try + { + _bitmap = new OpenGlTextureBitmap(); + } + catch (PlatformNotSupportedException) + { + _context.Dispose(); + _context = null; + return false; + } + + using (_context.MakeCurrent()) + { + _oldSize = Bounds.Size; + var gl = _context.Display.GlInterface; + var oneArr = new int[1]; + gl.GenFramebuffers(1, oneArr); + _fb = oneArr[0]; + gl.BindFramebuffer(GL_FRAMEBUFFER, _fb); + + gl.GenTextures(1, oneArr); + _texture = oneArr[0]; + gl.BindTexture(GL_TEXTURE_2D, _texture); + ResizeTexture(gl); + + gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0); + gl.DrawBuffers(1, new[] { GL_COLOR_ATTACHMENT0 }); + var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) + { + //TODO: Cleanup + return false; + } + OnOpenGlInit(_context.Display.GlInterface, _fb); + } + + return true; + } + + void ResizeTexture(GlInterface gl) + { + var size = GetPixelSize(); + gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, + size.Width, size.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + //TODO: destroy the previous one + var oneArr = new int[1]; + gl.GenRenderbuffers(1, oneArr); + _renderBuffer = oneArr[0]; + gl.BindRenderbuffer(GL_RENDERBUFFER, _renderBuffer); + gl.RenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.Width, size.Height); + gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderBuffer); + using (_bitmap.Lock()) + _bitmap.SetTexture(_texture, GL_RGBA8, size, 1); + } + + //TODO: dpi + PixelSize GetPixelSize() => + new PixelSize(Math.Max(1, (int)Bounds.Width), + Math.Max(1, (int)Bounds.Height)); + + + protected virtual void OnOpenGlInit(GlInterface gl, int fb) + { + + } + + protected virtual void OnOpenGlDeinit(GlInterface gl, int fb) + { + + } + + protected abstract void OnOpenGlRender(GlInterface gl, int fb); + } +} diff --git a/src/Avalonia.X11/Glx/GlxDisplay.cs b/src/Avalonia.X11/Glx/GlxDisplay.cs index 4b78c40737..2522ff35ab 100644 --- a/src/Avalonia.X11/Glx/GlxDisplay.cs +++ b/src/Avalonia.X11/Glx/GlxDisplay.cs @@ -10,7 +10,7 @@ namespace Avalonia.X11.Glx private readonly X11Info _x11; private readonly IntPtr _fbconfig; private readonly XVisualInfo* _visual; - public GlDisplayType Type => GlDisplayType.OpenGL2; + public GlDisplayType Type => GlDisplayType.OpenGl; public GlInterface GlInterface { get; } public XVisualInfo* VisualInfo => _visual; diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs index 3a7eec134e..76c149e53b 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs @@ -16,7 +16,7 @@ namespace Avalonia.Skia using (immediateContext.MakeCurrent()) { var display = gl.Display; - using (var iface = display.Type == GlDisplayType.OpenGL2 ? + using (var iface = display.Type == GlDisplayType.OpenGl ? GRGlInterface.AssembleGlInterface((_, proc) => display.GlInterface.GetProcAddress(proc)) : GRGlInterface.AssembleGlesInterface((_, proc) => display.GlInterface.GetProcAddress(proc))) {