diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj
index 18d222d8097..edcbcbe988d 100644
--- a/samples/ControlCatalog/ControlCatalog.csproj
+++ b/samples/ControlCatalog/ControlCatalog.csproj
@@ -30,10 +30,12 @@
+
+
diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index 4588b979bee..5e47f36c0bb 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -127,6 +127,9 @@
+
+
+
diff --git a/samples/ControlCatalog/Pages/OpenGl/GlPageKnobs.xaml b/samples/ControlCatalog/Pages/OpenGl/GlPageKnobs.xaml
new file mode 100644
index 00000000000..064d6cf1215
--- /dev/null
+++ b/samples/ControlCatalog/Pages/OpenGl/GlPageKnobs.xaml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+ Yaw
+
+ Pitch
+
+ Roll
+
+
+ D
+ I
+ S
+ C
+ O
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/ControlCatalog/Pages/OpenGl/GlPageKnobs.xaml.cs b/samples/ControlCatalog/Pages/OpenGl/GlPageKnobs.xaml.cs
new file mode 100644
index 00000000000..a4e2964ba12
--- /dev/null
+++ b/samples/ControlCatalog/Pages/OpenGl/GlPageKnobs.xaml.cs
@@ -0,0 +1,70 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace ControlCatalog.Pages.OpenGl;
+
+public partial class GlPageKnobs : UserControl
+{
+ public GlPageKnobs()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ 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);
+ }
+
+
+ private float _disco;
+
+ public static readonly DirectProperty DiscoProperty =
+ AvaloniaProperty.RegisterDirect("Disco", o => o.Disco, (o, v) => o.Disco = v);
+
+ public float Disco
+ {
+ get => _disco;
+ set => SetAndRaise(DiscoProperty, ref _disco, value);
+ }
+
+ private string _info = string.Empty;
+
+ public static readonly DirectProperty InfoProperty =
+ AvaloniaProperty.RegisterDirect("Info", o => o.Info, (o, v) => o.Info = v);
+
+ public string Info
+ {
+ get => _info;
+ private set => SetAndRaise(InfoProperty, ref _info, value);
+ }
+}
\ No newline at end of file
diff --git a/samples/ControlCatalog/Pages/OpenGl/OpenGlContent.cs b/samples/ControlCatalog/Pages/OpenGl/OpenGlContent.cs
new file mode 100644
index 00000000000..c975ca1e85b
--- /dev/null
+++ b/samples/ControlCatalog/Pages/OpenGl/OpenGlContent.cs
@@ -0,0 +1,311 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using Avalonia;
+using Avalonia.OpenGL;
+using static Avalonia.OpenGL.GlConsts;
+// ReSharper disable StringLiteralTypo
+
+namespace ControlCatalog.Pages.OpenGl;
+
+internal class OpenGlContent
+{
+ private int _vertexShader;
+ private int _fragmentShader;
+ private int _shaderProgram;
+ private int _vertexBufferObject;
+ private int _indexBufferObject;
+ private int _vertexArrayObject;
+ private GlVersion GlVersion;
+
+ private string GetShader(bool fragment, string shader)
+ {
+ var version = (GlVersion.Type == GlProfileType.OpenGL
+ ? RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 150 : 120
+ : 100);
+ var data = "#version " + version + "\n";
+ if (GlVersion.Type == GlProfileType.OpenGLES)
+ data += "precision mediump float;\n";
+ if (version >= 150)
+ {
+ shader = shader.Replace("attribute", "in");
+ if (fragment)
+ shader = shader
+ .Replace("varying", "in")
+ .Replace("//DECLAREGLFRAG", "out vec4 outFragColor;")
+ .Replace("gl_FragColor", "outFragColor");
+ else
+ shader = shader.Replace("varying", "out");
+ }
+
+ data += shader;
+
+ return data;
+ }
+
+
+ private string VertexShaderSource => GetShader(false, @"
+ attribute vec3 aPos;
+ attribute vec3 aNormal;
+ uniform mat4 uModel;
+ uniform mat4 uProjection;
+ uniform mat4 uView;
+
+ varying vec3 FragPos;
+ varying vec3 VecPos;
+ varying vec3 Normal;
+ uniform float uTime;
+ uniform float uDisco;
+ void main()
+ {
+ float discoScale = sin(uTime * 10.0) / 10.0;
+ float distortionX = 1.0 + uDisco * cos(uTime * 20.0) / 10.0;
+
+ float scale = 1.0 + uDisco * discoScale;
+
+ vec3 scaledPos = aPos;
+ scaledPos.x = scaledPos.x * distortionX;
+
+ scaledPos *= scale;
+ gl_Position = uProjection * uView * uModel * vec4(scaledPos, 1.0);
+ FragPos = vec3(uModel * vec4(aPos, 1.0));
+ VecPos = aPos;
+ Normal = normalize(vec3(uModel * vec4(aNormal, 1.0)));
+ }
+");
+
+ private string FragmentShaderSource => GetShader(true, @"
+ varying vec3 FragPos;
+ varying vec3 VecPos;
+ varying vec3 Normal;
+ uniform float uMaxY;
+ uniform float uMinY;
+ uniform float uTime;
+ uniform float uDisco;
+ //DECLAREGLFRAG
+
+ void main()
+ {
+ float y = (VecPos.y - uMinY) / (uMaxY - uMinY);
+ float c = cos(atan(VecPos.x, VecPos.z) * 20.0 + uTime * 40.0 + y * 50.0);
+ float s = sin(-atan(VecPos.z, VecPos.x) * 20.0 - uTime * 20.0 - y * 30.0);
+
+ vec3 discoColor = vec3(
+ 0.5 + abs(0.5 - y) * cos(uTime * 10.0),
+ 0.25 + (smoothstep(0.3, 0.8, y) * (0.5 - c / 4.0)),
+ 0.25 + abs((smoothstep(0.1, 0.4, y) * (0.5 - s / 4.0))));
+
+ vec3 objectColor = vec3((1.0 - y), 0.40 + y / 4.0, y * 0.75 + 0.25);
+ objectColor = objectColor * (1.0 - uDisco) + discoColor * uDisco;
+
+ float ambientStrength = 0.3;
+ vec3 lightColor = vec3(1.0, 1.0, 1.0);
+ vec3 lightPos = vec3(uMaxY * 2.0, uMaxY * 2.0, uMaxY * 2.0);
+ 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);
+
+ }
+");
+
+ [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 OpenGlContent()
+ {
+ 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 static void CheckError(GlInterface gl)
+ {
+ int err;
+ while ((err = gl.GetError()) != GL_NO_ERROR)
+ Console.WriteLine(err);
+ }
+
+ public string Info { get; private set; }
+
+ public unsafe void Init(GlInterface GL, GlVersion version)
+ {
+ GlVersion = version;
+ CheckError(GL);
+
+ Info = $"Renderer: {GL.GetString(GL_RENDERER)} Version: {GL.GetString(GL_VERSION)}";
+
+ // 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);
+ CheckError(GL);
+ 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);
+ CheckError(GL);
+ fixed (void* pdata = _indices)
+ GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(_indices.Length * sizeof(ushort)), new IntPtr(pdata),
+ GL_STATIC_DRAW);
+ CheckError(GL);
+ _vertexArrayObject = GL.GenVertexArray();
+ GL.BindVertexArray(_vertexArrayObject);
+ CheckError(GL);
+ 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);
+
+ }
+
+ public void Deinit(GlInterface GL)
+ {
+ // 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);
+ }
+
+ static Stopwatch St = Stopwatch.StartNew();
+
+ public unsafe void OnOpenGlRender(GlInterface gl, int fb, PixelSize size,
+ float yaw, float pitch, float roll, float disco)
+ {
+ gl.Viewport(0, 0, size.Width, size.Height);
+ gl.ClearDepth(1);
+ gl.Disable(GL_CULL_FACE);
+ gl.Disable(GL_SCISSOR_TEST);
+ gl.DepthFunc(GL_LESS);
+ gl.DepthMask(1);
+
+ gl.ClearColor(0, 0, 0, 0);
+ gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ gl.Enable(GL_DEPTH_TEST);
+
+
+ var GL = gl;
+
+ GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject);
+ GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject);
+ GL.BindVertexArray(_vertexArrayObject);
+ GL.UseProgram(_shaderProgram);
+ CheckError(GL);
+ var projection =
+ Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)(size.Width / size.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");
+ var timeLoc = GL.GetUniformLocationString(_shaderProgram, "uTime");
+ var discoLoc = GL.GetUniformLocationString(_shaderProgram, "uDisco");
+ 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.Uniform1f(timeLoc, (float)St.Elapsed.TotalSeconds);
+ GL.Uniform1f(discoLoc, disco);
+ CheckError(GL);
+ GL.DrawElements(GL_TRIANGLES, _indices.Length, GL_UNSIGNED_SHORT, IntPtr.Zero);
+
+ CheckError(GL);
+ }
+}
\ No newline at end of file
diff --git a/samples/ControlCatalog/Pages/OpenGl/OpenGlFbo.cs b/samples/ControlCatalog/Pages/OpenGl/OpenGlFbo.cs
new file mode 100644
index 00000000000..c6cc7d920fc
--- /dev/null
+++ b/samples/ControlCatalog/Pages/OpenGl/OpenGlFbo.cs
@@ -0,0 +1,118 @@
+using System;
+using Avalonia;
+using Avalonia.Logging;
+using Avalonia.OpenGL;
+using SkiaSharp;
+using static Avalonia.OpenGL.GlConsts;
+namespace ControlCatalog.Pages.OpenGl;
+
+internal class OpenGlFbo : IDisposable
+{
+ private readonly GRContext _grContext;
+ private int _fbo;
+ private int _depthBuffer;
+ private int _texture;
+ private PixelSize _size;
+ public PixelSize Size => _size;
+ public GlInterface Gl => Context.GlInterface;
+ public IGlContext Context { get; }
+
+ public OpenGlFbo(IGlContext context, GRContext grContext)
+ {
+ _grContext = grContext;
+ Context = context;
+ _fbo = Gl.GenFramebuffer();
+ }
+
+ public void Resize(PixelSize size)
+ {
+ if(_size == size)
+ return;
+
+ if (_texture != 0)
+ Gl.DeleteTexture(_texture);
+ _texture = 0;
+ if(_depthBuffer != 0)
+ Gl.DeleteRenderbuffer(_depthBuffer);
+ _depthBuffer = 0;
+ Gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo);
+
+ _texture = Gl.GenTexture();
+
+ var textureFormat = Context.Version.Type == GlProfileType.OpenGLES && Context.Version.Major == 2
+ ? GL_RGBA
+ : GL_RGBA8;
+
+ Gl.BindTexture(GL_TEXTURE_2D, _texture);
+ Gl.TexImage2D(GL_TEXTURE_2D, 0, textureFormat, size.Width, size.Height, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, IntPtr.Zero);
+ Gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
+
+ _depthBuffer = Gl.GenRenderbuffer();
+ var depthFormat = Context.Version.Type == GlProfileType.OpenGLES
+ ? GL_DEPTH_COMPONENT16
+ : GL_DEPTH_COMPONENT;
+ Gl.BindRenderbuffer(GL_RENDERBUFFER, _depthBuffer);
+ Gl.RenderbufferStorage(GL_RENDERBUFFER, depthFormat, size.Width, size.Height);
+ Gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer);
+
+ var status = Gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
+ IsValid = (status == GL_FRAMEBUFFER_COMPLETE);
+ if(!IsValid)
+ {
+ int code = Gl.GetError();
+ Console.WriteLine("Unable to configure OpenGL FBO: " + code);
+ }
+
+ _size = size;
+ }
+
+ public bool IsValid { get; private set; }
+
+ public int Fbo => _fbo;
+
+ public SKImage? Snapshot()
+ {
+ Gl.Flush();
+ _grContext.ResetContext();
+
+ using var texture = new GRBackendTexture(_size.Width, _size.Height, false,
+ new GRGlTextureInfo(GlConsts.GL_TEXTURE_2D, (uint)_texture, SKColorType.Rgba8888.ToGlSizedFormat()));
+
+ var surf = SKSurface.Create(_grContext, texture, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888);
+ if (surf == null)
+ {
+ using var unformatted = new GRBackendTexture(_size.Width, _size.Height, false,
+ new GRGlTextureInfo(GlConsts.GL_TEXTURE_2D, (uint)_texture));
+
+ surf = SKSurface.Create(_grContext, unformatted, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888);
+ }
+
+ SKImage? rv;
+ using (surf)
+ rv = surf?.Snapshot();
+ _grContext.Flush();
+ return rv;
+ /*
+ var target = new GRBackendRenderTarget(_size.Width, _size.Height, 0, 0,
+ new GRGlFramebufferInfo((uint)_fbo, SKColorType.Rgba8888.ToGlSizedFormat()));
+ SKImage rv;
+ using (var surface = SKSurface.Create(_grContext, target,
+ GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888,
+ new SKSurfaceProperties(SKPixelGeometry.RgbHorizontal)))
+ rv = surface.Snapshot();
+ _grContext.Flush();
+ return rv;*/
+ }
+
+ public void Dispose()
+ {
+ if(_fbo != 0)
+ Gl.DeleteFramebuffer(_fbo);
+ _fbo = 0;
+ if (_depthBuffer != 0)
+ Gl.DeleteRenderbuffer(_depthBuffer);
+ if(_texture != 0)
+ Gl.DeleteTexture(_texture);
+ }
+}
\ No newline at end of file
diff --git a/samples/ControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml b/samples/ControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml
new file mode 100644
index 00000000000..ffa7cad4d86
--- /dev/null
+++ b/samples/ControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/ControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml.cs b/samples/ControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml.cs
new file mode 100644
index 00000000000..91c4ce0dbe9
--- /dev/null
+++ b/samples/ControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml.cs
@@ -0,0 +1,216 @@
+using System;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.LogicalTree;
+using Avalonia.Markup.Xaml;
+using Avalonia.Media;
+using Avalonia.OpenGL;
+using Avalonia.Platform;
+using Avalonia.Rendering.Composition;
+using Avalonia.Skia;
+using ControlCatalog.Pages.OpenGl;
+using SkiaSharp;
+using static Avalonia.OpenGL.GlConsts;
+
+namespace ControlCatalog.Pages;
+
+public class OpenGlLeasePage : UserControl
+{
+ private readonly Control _viewport;
+ private readonly GlPageKnobs _knobs;
+ private CompositionCustomVisual? _visual;
+
+ class GlVisual : CompositionCustomVisualHandler
+ {
+ private OpenGlContent _content;
+ private Parameters _parameters;
+ private bool _contentInitialized;
+ private OpenGlFbo? _fbo;
+ private bool _reRender;
+ private IGlContext? _gl;
+
+ public GlVisual(OpenGlContent content, Parameters parameters)
+ {
+ _content = content;
+ _parameters = parameters;
+ }
+
+ public override void OnRender(ImmediateDrawingContext drawingContext)
+ {
+ if (_parameters.Disco > 0.01f)
+ RegisterForNextAnimationFrameUpdate();
+ var bounds = GetRenderBounds();
+ var size = PixelSize.FromSize(bounds.Size, 1);
+ if (size.Width < 1 || size.Height < 1)
+ return;
+
+ if(drawingContext.TryGetFeature(out var skiaFeature))
+ {
+ using var skiaLease = skiaFeature.Lease();
+ var grContext = skiaLease.GrContext;
+ if (grContext == null)
+ return;
+ SKImage? snapshot;
+ using (var platformApiLease = skiaLease.TryLeasePlatformGraphicsApi())
+ {
+ if (platformApiLease?.Context is not IGlContext glContext)
+ return;
+
+ var gl = glContext.GlInterface;
+ if (_gl != glContext)
+ {
+ // The old context is lost
+ _fbo = null;
+ _contentInitialized = false;
+ _gl = glContext;
+ }
+
+ gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var oldFb);
+
+ _fbo ??= new OpenGlFbo(glContext, grContext);
+ if (_fbo.Size != size)
+ _fbo.Resize(size);
+
+ gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo.Fbo);
+
+
+ if (!_contentInitialized)
+ {
+ _content.Init(gl, glContext.Version);
+ _contentInitialized = true;
+ }
+
+
+
+ _content.OnOpenGlRender(gl, _fbo.Fbo, size, _parameters.Yaw, _parameters.Pitch,
+ _parameters.Roll, _parameters.Disco);
+
+ snapshot = _fbo.Snapshot();
+ gl.BindFramebuffer(GL_FRAMEBUFFER, oldFb);
+ }
+
+ using(snapshot)
+ if (snapshot != null)
+ skiaLease.SkCanvas.DrawImage(snapshot, new SKRect(0, 0,
+ (float)bounds.Width, (float)bounds.Height));
+ }
+ }
+
+ public override void OnAnimationFrameUpdate()
+ {
+ if (_reRender || _parameters.Disco > 0.01f)
+ {
+ _reRender = false;
+ Invalidate();
+ }
+
+ base.OnAnimationFrameUpdate();
+ }
+
+ public override void OnMessage(object message)
+ {
+ if (message is Parameters p)
+ {
+ _parameters = p;
+ _reRender = true;
+ RegisterForNextAnimationFrameUpdate();
+ }
+ else if (message is DisposeMessage)
+ {
+ if (_gl != null)
+ {
+ try
+ {
+ if (_fbo != null || _contentInitialized)
+ {
+ using (_gl.MakeCurrent())
+ {
+ if (_contentInitialized)
+ _content.Deinit(_gl.GlInterface);
+ _contentInitialized = false;
+ _fbo?.Dispose();
+ _fbo = null;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.ToString());
+ }
+
+ _gl = null;
+ }
+ }
+
+ base.OnMessage(message);
+ }
+ }
+
+ public class Parameters
+ {
+ public float Yaw;
+ public float Pitch;
+ public float Roll;
+ public float Disco;
+ }
+
+ public class DisposeMessage
+ {
+
+ }
+
+ public OpenGlLeasePage()
+ {
+ AvaloniaXamlLoader.Load(this);
+ _viewport = this.FindControl("Viewport")!;
+ _viewport.AttachedToVisualTree += ViewportAttachedToVisualTree;
+ _viewport.DetachedFromVisualTree += ViewportDetachedFromVisualTree;
+ _knobs = this.FindControl("Knobs")!;
+ _knobs.PropertyChanged += KnobsPropertyChanged;
+ }
+
+ private void KnobsPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs change)
+ {
+ if (change.Property == GlPageKnobs.YawProperty
+ || change.Property == GlPageKnobs.RollProperty
+ || change.Property == GlPageKnobs.PitchProperty
+ || change.Property == GlPageKnobs.DiscoProperty)
+ _visual?.SendHandlerMessage(GetParameters());
+ }
+
+ Parameters GetParameters() => new()
+ {
+ Yaw = _knobs!.Yaw, Pitch = _knobs.Pitch, Roll = _knobs.Roll, Disco = _knobs.Disco
+ };
+
+ private void ViewportAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
+ {
+ var visual = ElementComposition.GetElementVisual(_viewport!);
+ if(visual == null)
+ return;
+ _visual = visual.Compositor.CreateCustomVisual(new GlVisual(new OpenGlContent(), GetParameters()));
+ ElementComposition.SetElementChildVisual(_viewport, _visual);
+ UpdateSize(Bounds.Size);
+ }
+
+ private void UpdateSize(Size size)
+ {
+ if (_visual != null)
+ _visual.Size = new Vector(size.Width, size.Height);
+ }
+
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ var size = base.ArrangeOverride(finalSize);
+ UpdateSize(size);
+ return size;
+ }
+
+ private void ViewportDetachedFromVisualTree(object sender, VisualTreeAttachmentEventArgs e)
+ {
+ _visual?.SendHandlerMessage(new DisposeMessage());
+ _visual = null;
+ ElementComposition.SetElementChildVisual(_viewport, null);
+ base.OnDetachedFromVisualTree(e);
+ }
+}
\ No newline at end of file
diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml b/samples/ControlCatalog/Pages/OpenGlPage.xaml
index 2a97956e308..2d1285566c5 100644
--- a/samples/ControlCatalog/Pages/OpenGlPage.xaml
+++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml
@@ -1,29 +1,10 @@
+ xmlns:pages="using:ControlCatalog.Pages"
+ xmlns:openGl="clr-namespace:ControlCatalog.Pages.OpenGl">
-
-
-
-
-
- Yaw
-
- Pitch
-
- Roll
-
-
- D
- I
- S
- C
- O
-
-
-
-
+
diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
index c77d65ddf19..494a672b947 100644
--- a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
@@ -1,375 +1,56 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Numerics;
-using System.Runtime.InteropServices;
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Controls;
-using Avalonia.Platform.Interop;
-using Avalonia.Threading;
-using static Avalonia.OpenGL.GlConsts;
+using ControlCatalog.Pages.OpenGl;
+
// ReSharper disable StringLiteralTypo
namespace ControlCatalog.Pages
{
public class OpenGlPage : UserControl
{
-
+ public OpenGlPage()
+ {
+ AvaloniaXamlLoader.Load(this);
+ this.FindControl("GL")
+ !.Init(this.FindControl("Knobs")!);
+ }
}
public class OpenGlPageControl : OpenGlControlBase
{
- private float _yaw;
-
- public static readonly DirectProperty YawProperty =
- AvaloniaProperty.RegisterDirect("Yaw", o => o.Yaw, (o, v) => o.Yaw = v);
+ private OpenGlContent _content = new();
+ private GlPageKnobs? _knobs;
- public float Yaw
+ public void Init(GlPageKnobs knobs)
{
- get => _yaw;
- set => SetAndRaise(YawProperty, ref _yaw, value);
+ _knobs = knobs;
+ _knobs.PropertyChanged += KnobsPropertyChanged;
}
- private float _pitch;
-
- public static readonly DirectProperty PitchProperty =
- AvaloniaProperty.RegisterDirect("Pitch", o => o.Pitch, (o, v) => o.Pitch = v);
-
- public float Pitch
+ private void KnobsPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs change)
{
- 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);
- }
-
-
- private float _disco;
-
- public static readonly DirectProperty DiscoProperty =
- AvaloniaProperty.RegisterDirect("Disco", o => o.Disco, (o, v) => o.Disco = v);
-
- public float Disco
- {
- get => _disco;
- set => SetAndRaise(DiscoProperty, ref _disco, value);
- }
-
- private string _info = string.Empty;
-
- public static readonly DirectProperty InfoProperty =
- AvaloniaProperty.RegisterDirect("Info", o => o.Info, (o, v) => o.Info = v);
-
- public string Info
- {
- get => _info;
- private set => SetAndRaise(InfoProperty, ref _info, value);
+ if (change.Property == GlPageKnobs.YawProperty
+ || change.Property == GlPageKnobs.RollProperty
+ || change.Property == GlPageKnobs.PitchProperty
+ || change.Property == GlPageKnobs.DiscoProperty)
+ RequestNextFrameRendering();
}
- private int _vertexShader;
- private int _fragmentShader;
- private int _shaderProgram;
- private int _vertexBufferObject;
- private int _indexBufferObject;
- private int _vertexArrayObject;
-
- private string GetShader(bool fragment, string shader)
- {
- var version = (GlVersion.Type == GlProfileType.OpenGL ?
- RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 150 : 120 :
- 100);
- var data = "#version " + version + "\n";
- if (GlVersion.Type == GlProfileType.OpenGLES)
- data += "precision mediump float;\n";
- if (version >= 150)
- {
- shader = shader.Replace("attribute", "in");
- if (fragment)
- shader = shader
- .Replace("varying", "in")
- .Replace("//DECLAREGLFRAG", "out vec4 outFragColor;")
- .Replace("gl_FragColor", "outFragColor");
- else
- shader = shader.Replace("varying", "out");
- }
-
- data += shader;
-
- return data;
- }
-
-
- private string VertexShaderSource => GetShader(false, @"
- attribute vec3 aPos;
- attribute vec3 aNormal;
- uniform mat4 uModel;
- uniform mat4 uProjection;
- uniform mat4 uView;
-
- varying vec3 FragPos;
- varying vec3 VecPos;
- varying vec3 Normal;
- uniform float uTime;
- uniform float uDisco;
- void main()
- {
- float discoScale = sin(uTime * 10.0) / 10.0;
- float distortionX = 1.0 + uDisco * cos(uTime * 20.0) / 10.0;
-
- float scale = 1.0 + uDisco * discoScale;
-
- vec3 scaledPos = aPos;
- scaledPos.x = scaledPos.x * distortionX;
-
- scaledPos *= scale;
- gl_Position = uProjection * uView * uModel * vec4(scaledPos, 1.0);
- FragPos = vec3(uModel * vec4(aPos, 1.0));
- VecPos = aPos;
- Normal = normalize(vec3(uModel * vec4(aNormal, 1.0)));
- }
-");
-
- private string FragmentShaderSource => GetShader(true, @"
- varying vec3 FragPos;
- varying vec3 VecPos;
- varying vec3 Normal;
- uniform float uMaxY;
- uniform float uMinY;
- uniform float uTime;
- uniform float uDisco;
- //DECLAREGLFRAG
-
- void main()
- {
- float y = (VecPos.y - uMinY) / (uMaxY - uMinY);
- float c = cos(atan(VecPos.x, VecPos.z) * 20.0 + uTime * 40.0 + y * 50.0);
- float s = sin(-atan(VecPos.z, VecPos.x) * 20.0 - uTime * 20.0 - y * 30.0);
-
- vec3 discoColor = vec3(
- 0.5 + abs(0.5 - y) * cos(uTime * 10.0),
- 0.25 + (smoothstep(0.3, 0.8, y) * (0.5 - c / 4.0)),
- 0.25 + abs((smoothstep(0.1, 0.4, y) * (0.5 - s / 4.0))));
-
- vec3 objectColor = vec3((1.0 - y), 0.40 + y / 4.0, y * 0.75 + 0.25);
- objectColor = objectColor * (1.0 - uDisco) + discoColor * uDisco;
-
- float ambientStrength = 0.3;
- vec3 lightColor = vec3(1.0, 1.0, 1.0);
- vec3 lightPos = vec3(uMaxY * 2.0, uMaxY * 2.0, uMaxY * 2.0);
- vec3 ambient = ambientStrength * lightColor;
+ protected override unsafe void OnOpenGlInit(GlInterface GL) => _content.Init(GL, GlVersion);
+ protected override void OnOpenGlDeinit(GlInterface GL) => _content.Deinit(GL);
- 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);
-
- }
-");
-
- [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 static void CheckError(GlInterface gl)
- {
- int err;
- while ((err = gl.GetError()) != GL_NO_ERROR)
- Console.WriteLine(err);
- }
-
- protected override unsafe void OnOpenGlInit(GlInterface GL)
- {
- CheckError(GL);
-
- Info = $"Renderer: {GL.GetString(GL_RENDERER)} Version: {GL.GetString(GL_VERSION)}";
-
- // 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);
- CheckError(GL);
- 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);
- CheckError(GL);
- fixed (void* pdata = _indices)
- GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(_indices.Length * sizeof(ushort)), new IntPtr(pdata),
- GL_STATIC_DRAW);
- CheckError(GL);
- _vertexArrayObject = GL.GenVertexArray();
- GL.BindVertexArray(_vertexArrayObject);
- CheckError(GL);
- 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)
- {
- // 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);
- }
-
- static Stopwatch St = Stopwatch.StartNew();
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);
- CheckError(GL);
- 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");
- var timeLoc = GL.GetUniformLocationString(_shaderProgram, "uTime");
- var discoLoc = GL.GetUniformLocationString(_shaderProgram, "uDisco");
- 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.Uniform1f(timeLoc, (float)St.Elapsed.TotalSeconds);
- GL.Uniform1f(discoLoc, _disco);
- CheckError(GL);
- GL.DrawElements(GL_TRIANGLES, _indices.Length, GL_UNSIGNED_SHORT, IntPtr.Zero);
-
- CheckError(GL);
- if (_disco > 0.01)
- RequestNextFrameRendering();
- }
-
- protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
- {
- if (change.Property == YawProperty || change.Property == RollProperty || change.Property == PitchProperty ||
- change.Property == DiscoProperty)
+ if (_knobs == null)
+ return;
+ _content.OnOpenGlRender(gl, fb, new PixelSize((int)Bounds.Width, (int)Bounds.Height),
+ _knobs.Yaw, _knobs.Pitch, _knobs.Roll, _knobs.Disco);
+ if (_knobs.Disco > 0.01)
RequestNextFrameRendering();
- base.OnPropertyChanged(change);
}
}
}
diff --git a/samples/ControlCatalog/Pages/ProgressBarPage.xaml b/samples/ControlCatalog/Pages/ProgressBarPage.xaml
index dcddcd59656..add3f976e2f 100644
--- a/samples/ControlCatalog/Pages/ProgressBarPage.xaml
+++ b/samples/ControlCatalog/Pages/ProgressBarPage.xaml
@@ -1,41 +1,11 @@
- A progress bar control
-
-
- Maximum
-
-
-
- Minimum
-
-
-
- Progress Text Format
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
diff --git a/src/Avalonia.OpenGL/GlBasicInfoInterface.cs b/src/Avalonia.OpenGL/GlBasicInfoInterface.cs
index 38969cc9668..750b34a13f0 100644
--- a/src/Avalonia.OpenGL/GlBasicInfoInterface.cs
+++ b/src/Avalonia.OpenGL/GlBasicInfoInterface.cs
@@ -15,6 +15,9 @@ public GlBasicInfoInterface(Func getProcAddress)
[GetProcAddress("glGetIntegerv")]
public partial void GetIntegerv(int name, out int rv);
+
+ [GetProcAddress("glGetFloatv")]
+ public partial void GetFloatv(int name, out float rv);
[GetProcAddress("glGetString")]
public partial IntPtr GetStringNative(int v);
diff --git a/src/Avalonia.OpenGL/GlConsts.cs b/src/Avalonia.OpenGL/GlConsts.cs
index 8f53e5a6faa..a841a195911 100644
--- a/src/Avalonia.OpenGL/GlConsts.cs
+++ b/src/Avalonia.OpenGL/GlConsts.cs
@@ -90,7 +90,7 @@ public static class GlConsts
// public const int GL_POLYGON_SMOOTH = 0x0B41;
// public const int GL_POLYGON_STIPPLE = 0x0B42;
// public const int GL_EDGE_FLAG = 0x0B43;
-// public const int GL_CULL_FACE = 0x0B44;
+ public const int GL_CULL_FACE = 0x0B44;
// public const int GL_CULL_FACE_MODE = 0x0B45;
// public const int GL_FRONT_FACE = 0x0B46;
// public const int GL_POLYGON_OFFSET_FACTOR = 0x8038;
@@ -104,10 +104,10 @@ public static class GlConsts
// public const int GL_LIST_INDEX = 0x0B33;
// public const int GL_LIST_MODE = 0x0B30;
// public const int GL_NEVER = 0x0200;
-// public const int GL_LESS = 0x0201;
+ public const int GL_LESS = 0x0201;
// public const int GL_EQUAL = 0x0202;
// public const int GL_LEQUAL = 0x0203;
-// public const int GL_GREATER = 0x0204;
+ public const int GL_GREATER = 0x0204;
// public const int GL_NOTEQUAL = 0x0205;
// public const int GL_GEQUAL = 0x0206;
// public const int GL_ALWAYS = 0x0207;
@@ -359,7 +359,7 @@ public static class GlConsts
// public const int GL_FASTEST = 0x1101;
// public const int GL_NICEST = 0x1102;
// public const int GL_SCISSOR_BOX = 0x0C10;
-// public const int GL_SCISSOR_TEST = 0x0C11;
+ public const int GL_SCISSOR_TEST = 0x0C11;
// public const int GL_MAP_COLOR = 0x0D10;
// public const int GL_MAP_STENCIL = 0x0D11;
// public const int GL_INDEX_SHIFT = 0x0D12;
diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs
index 248b20c774c..618d8a0537c 100644
--- a/src/Avalonia.OpenGL/GlInterface.cs
+++ b/src/Avalonia.OpenGL/GlInterface.cs
@@ -60,6 +60,26 @@ public GlInterface(GlVersion version, Func getProcAddress) : thi
[GetProcAddress("glClearColor")]
public partial void ClearColor(float r, float g, float b, float a);
+
+ [GetProcAddress("glClearDepth", true)]
+ internal partial void ClearDepthDouble(double value);
+
+ [GetProcAddress("glClearDepthf", true)]
+ internal partial void ClearDepthFloat(float value);
+
+ public void ClearDepth(float value)
+ {
+ if(IsClearDepthDoubleAvailable)
+ ClearDepthDouble(value);
+ else if (IsClearDepthFloatAvailable)
+ _addr_ClearDepthFloat(value);
+ }
+
+ [GetProcAddress("glDepthFunc")]
+ public partial void DepthFunc(int value);
+
+ [GetProcAddress("glDepthMask")]
+ public partial void DepthMask(int value);
[GetProcAddress("glClear")]
public partial void Clear(int bits);
@@ -319,6 +339,9 @@ public int GetUniformLocationString(int program, string name)
[GetProcAddress("glEnable")]
public partial void Enable(int what);
+
+ [GetProcAddress("glDisable")]
+ public partial void Disable(int what);
[GetProcAddress("glDeleteBuffers")]
public partial void DeleteBuffers(int count, int* buffers);
diff --git a/src/tools/DevGenerators/GetProcAddressInitialization.cs b/src/tools/DevGenerators/GetProcAddressInitialization.cs
index e8d7c251fab..2983010b1c7 100644
--- a/src/tools/DevGenerators/GetProcAddressInitialization.cs
+++ b/src/tools/DevGenerators/GetProcAddressInitialization.cs
@@ -70,7 +70,12 @@ string GetContextName(string type)
var isOptional = false;
var first = true;
var fieldName = "_addr_" + method.Name;
- var delegateType = BuildDelegateType(method);
+ var delegateType = BuildDelegateType(classBuilder, method, fieldName);
+ var visibility = method.DeclaredAccessibility == Accessibility.Public
+ ? "public "
+ : method.DeclaredAccessibility == Accessibility.Internal
+ ? "internal "
+ : "";
void AppendNextAddr()
{
@@ -160,14 +165,8 @@ void AppendNextAddr()
classBuilder
.Pad(1)
- .Append(delegateType);
- classBuilder
- .Append(fieldName)
- .AppendLine(";");
-
- classBuilder
- .Pad(1)
- .Append("public partial ")
+ .Append(visibility)
+ .Append(" partial ")
.Append(method.ReturnType.GetFullyQualifiedName())
.Append(" ")
.Append(method.Name)
@@ -238,7 +237,8 @@ void AppendNextAddr()
if (isOptional)
classBuilder
.Pad(1)
- .Append("public bool Is")
+ .Append(visibility)
+ .Append(" bool Is")
.Append(method.Name)
.Append("Available => ")
.Append(fieldName)
@@ -313,19 +313,41 @@ static string MapToNative(ITypeSymbol type)
return type.GetFullyQualifiedName();
}
- static string BuildDelegateType(IMethodSymbol method)
+ static string BuildDelegateType(StringBuilder classBuilder, IMethodSymbol method, string fieldName)
{
- StringBuilder name = new("delegate* unmanaged[Stdcall]<");
- var firstArg = true;
+ StringBuilder functionPointer = new("delegate* unmanaged[Stdcall]<");
+ // We need this one because Mono interpreter needs pre-generated trampolines for function pointers,
+ // but .NET WASM SDK doesn't actually scan method bodies for calli instructions and only
+ // looks for methods with DllImport and delegates with UnmanagedFunctionPointer
+ StringBuilder fakeDelegate = new(
+ " [global::System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(global::System.Runtime.InteropServices.CallingConvention.Cdecl)]\n internal delegate ");
+ fakeDelegate
+ .Append(MapToNative(method.ReturnType))
+ .Append(" __wasmDummy")
+ .Append(method.Name)
+ .Append("(");
+
+
+ int arg = 0;
- void AppendArg(string a, RefKind kind)
+ void AppendArgCore(StringBuilder builder, string a, RefKind kind, bool isFirstArg)
{
- if (firstArg)
- firstArg = false;
- else
- name.Append(",");
- AppendRefKind(name, kind);
- name.Append(a);
+ if (!isFirstArg)
+ builder.Append(",");
+ AppendRefKind(builder, kind);
+ builder.Append(a);
+ }
+
+ void AppendArg(string a, RefKind kind, bool returnArg = false)
+ {
+ AppendArgCore(functionPointer, a, kind, arg == 0);
+ if (!returnArg)
+ {
+ AppendArgCore(fakeDelegate, a, kind, arg == 0);
+ fakeDelegate.Append($" a{arg}");
+ }
+
+ arg++;
}
foreach (var p in method.Parameters)
@@ -333,9 +355,18 @@ void AppendArg(string a, RefKind kind)
AppendArg(MapToNative(p.Type), p.RefKind);
}
- AppendArg(MapToNative(method.ReturnType), RefKind.None);
- name.Append(">");
- return name.ToString();
+ AppendArg(MapToNative(method.ReturnType), RefKind.None, true);
+ functionPointer.Append(">");
+ fakeDelegate.Append(");");
+
+ classBuilder
+ .Pad(1)
+ .Append(functionPointer);
+ classBuilder
+ .Append(fieldName)
+ .AppendLine(";");
+ classBuilder.AppendLine(fakeDelegate.ToString());
+ return functionPointer.ToString();
}
}