Browse Source

Implemented GlControlBase

Nikita Tsukanov 5 years ago
parent
commit
021995ed35

+ 3 - 1
samples/ControlCatalog/ControlCatalog.csproj

@@ -1,6 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>    
+    <TargetFramework>netstandard2.0</TargetFramework>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>    
   </PropertyGroup>
   <ItemGroup>
     <Compile Update="**\*.xaml.cs">
@@ -17,6 +18,7 @@
     <EmbeddedResource Include="Assets\Fonts\SourceSansPro-BoldItalic.ttf" />
     <EmbeddedResource Include="Assets\Fonts\SourceSansPro-Italic.ttf" />
     <EmbeddedResource Include="Assets\Fonts\SourceSansPro-Regular.ttf" />
+    <EmbeddedResource Include="Pages\teapot.bin" />
   </ItemGroup>
 
   <ItemGroup>

+ 1 - 0
samples/ControlCatalog/MainView.xaml

@@ -14,6 +14,7 @@
         </Style>
     </Grid.Styles>  
     <TabControl Classes="sidebar" Name="Sidebar">
+      <TabItem Header="OpenGL"><pages:OpenGlPage/></TabItem>
       <TabItem Header="AutoCompleteBox"><pages:AutoCompleteBoxPage/></TabItem>
       <TabItem Header="Border"><pages:BorderPage/></TabItem>
       <TabItem Header="Button"><pages:ButtonPage/></TabItem>

+ 18 - 0
samples/ControlCatalog/Pages/OpenGlPage.xaml

@@ -0,0 +1,18 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             x:Class="ControlCatalog.Pages.OpenGlPage"
+             xmlns:pages="clr-namespace:ControlCatalog.Pages">
+  <Grid>
+    <pages:OpenGlPageControl x:Name="GL"/>
+    <Grid ColumnDefinitions="*,Auto" Margin="20">
+      <StackPanel Grid.Column="1" MinWidth="300">
+        <TextBlock>Yaw</TextBlock>
+        <Slider Value="{Binding Yaw, Mode=TwoWay, ElementName=GL}" Maximum="10"/>
+        <TextBlock>Pitch</TextBlock>
+        <Slider Value="{Binding Pitch, Mode=TwoWay, ElementName=GL}" Maximum="10"/>
+        <TextBlock>Roll</TextBlock>
+        <Slider Value="{Binding Roll, Mode=TwoWay, ElementName=GL}" Maximum="10"/>
+      </StackPanel>
+    </Grid>
+  </Grid>
+</UserControl>

+ 288 - 0
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<OpenGlPageControl, float> YawProperty =
+            AvaloniaProperty.RegisterDirect<OpenGlPageControl, float>("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<OpenGlPageControl, float> PitchProperty =
+            AvaloniaProperty.RegisterDirect<OpenGlPageControl, float>("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<OpenGlPageControl, float> RollProperty =
+            AvaloniaProperty.RegisterDirect<OpenGlPageControl, float>("Roll", o => o.Roll, (o, v) => o.Roll = v);
+
+        public float Roll
+        {
+            get => _roll;
+            set => SetAndRaise(RollProperty, ref _roll, value);
+        }
+
+        static OpenGlPageControl()
+        {
+            AffectsRender<OpenGlPageControl>(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<Vertex>();
+            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);
+        }
+    }
+}

BIN
samples/ControlCatalog/Pages/teapot.bin


+ 1 - 1
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; }
 

+ 2 - 2
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
                 }
             })
             {

+ 3 - 3
src/Avalonia.OpenGL/GlDisplayType.cs

@@ -2,7 +2,7 @@ namespace Avalonia.OpenGL
 {
     public enum GlDisplayType
     {
-        OpenGL2,
-        OpenGLES2
+        OpenGl,
+        OpenGLES
     }
-}
+}

+ 7 - 2
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;
+        }
     }
 }

+ 212 - 0
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
     }
 }

+ 16 - 1
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));
                 }

+ 149 - 0
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<IWindowingPlatformGlFeature>();
+            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);
+    }
+}

+ 1 - 1
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;

+ 1 - 1
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)))
                 {