Browse Source

Feature/opengl lease demo (#15707)

* OpenGL via API lease example

* Hide glClearDepth internals

* Missing file
pull/15722/head
Nikita Tsukanov 2 years ago
committed by GitHub
parent
commit
3975dbe0a9
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      samples/ControlCatalog/ControlCatalog.csproj
  2. 3
      samples/ControlCatalog/MainView.xaml
  3. 30
      samples/ControlCatalog/Pages/OpenGl/GlPageKnobs.xaml
  4. 70
      samples/ControlCatalog/Pages/OpenGl/GlPageKnobs.xaml.cs
  5. 311
      samples/ControlCatalog/Pages/OpenGl/OpenGlContent.cs
  6. 118
      samples/ControlCatalog/Pages/OpenGl/OpenGlFbo.cs
  7. 10
      samples/ControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml
  8. 216
      samples/ControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml.cs
  9. 25
      samples/ControlCatalog/Pages/OpenGlPage.xaml
  10. 373
      samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
  11. 32
      samples/ControlCatalog/Pages/ProgressBarPage.xaml
  12. 3
      src/Avalonia.OpenGL/GlBasicInfoInterface.cs
  13. 8
      src/Avalonia.OpenGL/GlConsts.cs
  14. 23
      src/Avalonia.OpenGL/GlInterface.cs
  15. 77
      src/tools/DevGenerators/GetProcAddressInitialization.cs

2
samples/ControlCatalog/ControlCatalog.csproj

@ -30,10 +30,12 @@
<ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
<ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
<ProjectReference Include="..\SampleControls\ControlSamples.csproj" />
</ItemGroup>
<Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\build\SourceGenerators.props" />
<Import Project="..\..\build\SkiaSharp.props" />
</Project>

3
samples/ControlCatalog/MainView.xaml

@ -127,6 +127,9 @@
<TabItem Header="OpenGL">
<pages:OpenGlPage />
</TabItem>
<TabItem Header="OpenGL Lease">
<pages:OpenGlLeasePage />
</TabItem>
<TabItem Header="Platform Information">
<pages:PlatformInfoPage />
</TabItem>

30
samples/ControlCatalog/Pages/OpenGl/GlPageKnobs.xaml

@ -0,0 +1,30 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.OpenGl.GlPageKnobs"
xmlns:pages="using:ControlCatalog.Pages"
xmlns:gl="using:ControlCatalog.Pages.OpenGl"
>
<Grid>
<StackPanel>
<TextBlock Text="{Binding $parent[gl:GlPageKnobs].Info}"/>
</StackPanel>
<Grid ColumnDefinitions="*,Auto" Margin="20">
<StackPanel Grid.Column="1" MinWidth="300">
<TextBlock>Yaw</TextBlock>
<Slider Value="{Binding $parent[gl:GlPageKnobs].Yaw, Mode=TwoWay}" Maximum="10"/>
<TextBlock>Pitch</TextBlock>
<Slider Value="{Binding $parent[gl:GlPageKnobs].Pitch, Mode=TwoWay}" Maximum="10"/>
<TextBlock>Roll</TextBlock>
<Slider Value="{Binding $parent[gl:GlPageKnobs].Roll, Mode=TwoWay}" Maximum="10"/>
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" Foreground="#C000C0">D</TextBlock>
<TextBlock FontWeight="Bold" Foreground="#00C090">I</TextBlock>
<TextBlock FontWeight="Bold" Foreground="#90C000">S</TextBlock>
<TextBlock FontWeight="Bold" Foreground="#C09000">C</TextBlock>
<TextBlock FontWeight="Bold" Foreground="#00C090">O</TextBlock>
</StackPanel>
<Slider Value="{Binding $parent[gl:GlPageKnobs].Disco, Mode=TwoWay}" Maximum="1"/>
</StackPanel>
</Grid>
</Grid>
</UserControl>

70
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<GlPageKnobs, float> YawProperty =
AvaloniaProperty.RegisterDirect<GlPageKnobs, 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<GlPageKnobs, float> PitchProperty =
AvaloniaProperty.RegisterDirect<GlPageKnobs, 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<GlPageKnobs, float> RollProperty =
AvaloniaProperty.RegisterDirect<GlPageKnobs, float>("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<GlPageKnobs, float> DiscoProperty =
AvaloniaProperty.RegisterDirect<GlPageKnobs, float>("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<GlPageKnobs, string> InfoProperty =
AvaloniaProperty.RegisterDirect<GlPageKnobs, string>("Info", o => o.Info, (o, v) => o.Info = v);
public string Info
{
get => _info;
private set => SetAndRaise(InfoProperty, ref _info, value);
}
}

311
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<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);
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);
}
}

118
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);
}
}

10
samples/ControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml

@ -0,0 +1,10 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.OpenGlLeasePage"
xmlns:pages="using:ControlCatalog.Pages"
xmlns:openGl="clr-namespace:ControlCatalog.Pages.OpenGl">
<Grid>
<Control x:Name="Viewport" />
<openGl:GlPageKnobs x:Name="Knobs" />
</Grid>
</UserControl>

216
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<ISkiaSharpApiLeaseFeature>(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<Control>("Viewport")!;
_viewport.AttachedToVisualTree += ViewportAttachedToVisualTree;
_viewport.DetachedFromVisualTree += ViewportDetachedFromVisualTree;
_knobs = this.FindControl<GlPageKnobs>("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);
}
}

25
samples/ControlCatalog/Pages/OpenGlPage.xaml

@ -1,29 +1,10 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.OpenGlPage"
xmlns:pages="using:ControlCatalog.Pages">
xmlns:pages="using:ControlCatalog.Pages"
xmlns:openGl="clr-namespace:ControlCatalog.Pages.OpenGl">
<Grid>
<pages:OpenGlPageControl x:Name="GL"/>
<StackPanel>
<TextBlock Text="{Binding #GL.Info}"/>
</StackPanel>
<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 Orientation="Horizontal">
<TextBlock FontWeight="Bold" Foreground="#C000C0">D</TextBlock>
<TextBlock FontWeight="Bold" Foreground="#00C090">I</TextBlock>
<TextBlock FontWeight="Bold" Foreground="#90C000">S</TextBlock>
<TextBlock FontWeight="Bold" Foreground="#C09000">C</TextBlock>
<TextBlock FontWeight="Bold" Foreground="#00C090">O</TextBlock>
</StackPanel>
<Slider Value="{Binding Disco, Mode=TwoWay, ElementName=GL}" Maximum="1"/>
</StackPanel>
</Grid>
<openGl:GlPageKnobs x:Name="Knobs" />
</Grid>
</UserControl>

373
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<OpenGlPageControl>("GL")
!.Init(this.FindControl<GlPageKnobs>("Knobs")!);
}
}
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);
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<OpenGlPageControl, float> PitchProperty =
AvaloniaProperty.RegisterDirect<OpenGlPageControl, float>("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<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);
}
private float _disco;
public static readonly DirectProperty<OpenGlPageControl, float> DiscoProperty =
AvaloniaProperty.RegisterDirect<OpenGlPageControl, float>("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<OpenGlPageControl, string> InfoProperty =
AvaloniaProperty.RegisterDirect<OpenGlPageControl, string>("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<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);
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);
}
}
}

32
samples/ControlCatalog/Pages/ProgressBarPage.xaml

@ -1,41 +1,11 @@
<UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ControlCatalog.Pages.ProgressBarPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">A progress bar control</TextBlock>
<StackPanel Spacing="5">
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBlock VerticalAlignment="Center">Maximum</TextBlock>
<NumericUpDown x:Name="maximum" Value="100" VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBlock VerticalAlignment="Center">Minimum</TextBlock>
<NumericUpDown x:Name="minimum" Value="0" VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBlock VerticalAlignment="Center">Progress Text Format</TextBlock>
<TextBox x:Name="stringFormat" Text="{}{0:0}%" VerticalAlignment="Center"/>
</StackPanel>
<CheckBox x:Name="showProgress" Margin="10,16,0,0" Content="Show Progress Text" />
<CheckBox x:Name="isIndeterminate" Margin="10,16,0,0" Content="Toggle Indeterminate" />
<StackPanel Orientation="Horizontal" Margin="0,16,0,0" HorizontalAlignment="Center" Spacing="16">
<StackPanel Spacing="16">
<ProgressBar IsIndeterminate="{Binding #isIndeterminate.IsChecked}" ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #hprogress.Value}"
Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}" ProgressTextFormat="{Binding #stringFormat.Text}"/>
</StackPanel>
<ProgressBar IsIndeterminate="{Binding #isIndeterminate.IsChecked}" ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #vprogress.Value}" Orientation="Vertical"
Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}" ProgressTextFormat="{Binding #stringFormat.Text}"/>
</StackPanel>
<StackPanel Margin="16">
<Slider Name="hprogress" Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}" Value="40" />
<Slider Name="vprogress" Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}" Value="60" />
</StackPanel>
<StackPanel Spacing="10">
<ProgressBar VerticalAlignment="Center" IsIndeterminate="True"
Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}"/>
<ProgressBar VerticalAlignment="Center" IsIndeterminate="True" />
<ProgressBar VerticalAlignment="Center" Value="5" Maximum="10" />
<ProgressBar VerticalAlignment="Center" Value="50" />
<ProgressBar VerticalAlignment="Center" Value="50" Minimum="25" Maximum="75" />
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

3
src/Avalonia.OpenGL/GlBasicInfoInterface.cs

@ -15,6 +15,9 @@ namespace Avalonia.OpenGL
[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);

8
src/Avalonia.OpenGL/GlConsts.cs

@ -90,7 +90,7 @@ namespace Avalonia.OpenGL
// 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 @@ namespace Avalonia.OpenGL
// 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 @@ namespace Avalonia.OpenGL
// 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;

23
src/Avalonia.OpenGL/GlInterface.cs

@ -60,6 +60,26 @@ namespace Avalonia.OpenGL
[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 @@ namespace Avalonia.OpenGL
[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);

77
src/tools/DevGenerators/GetProcAddressInitialization.cs

@ -70,7 +70,12 @@ public class GetProcAddressInitializationGenerator : IIncrementalGenerator
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 @@ public class GetProcAddressInitializationGenerator : IIncrementalGenerator
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 @@ public class GetProcAddressInitializationGenerator : IIncrementalGenerator
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 @@ public class GetProcAddressInitializationGenerator : IIncrementalGenerator
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 @@ public class GetProcAddressInitializationGenerator : IIncrementalGenerator
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();
}
}

Loading…
Cancel
Save