OpenGlLeasePage.xaml.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. using System;
  2. using Avalonia;
  3. using Avalonia.Controls;
  4. using Avalonia.LogicalTree;
  5. using Avalonia.Markup.Xaml;
  6. using Avalonia.Media;
  7. using Avalonia.OpenGL;
  8. using Avalonia.Platform;
  9. using Avalonia.Rendering.Composition;
  10. using Avalonia.Skia;
  11. using ControlCatalog.Pages.OpenGl;
  12. using SkiaSharp;
  13. using static Avalonia.OpenGL.GlConsts;
  14. namespace ControlCatalog.Pages;
  15. public class OpenGlLeasePage : UserControl
  16. {
  17. private readonly Control _viewport;
  18. private readonly GlPageKnobs _knobs;
  19. private CompositionCustomVisual? _visual;
  20. class GlVisual : CompositionCustomVisualHandler
  21. {
  22. private OpenGlContent _content;
  23. private Parameters _parameters;
  24. private bool _contentInitialized;
  25. private OpenGlFbo? _fbo;
  26. private bool _reRender;
  27. private IGlContext? _gl;
  28. public GlVisual(OpenGlContent content, Parameters parameters)
  29. {
  30. _content = content;
  31. _parameters = parameters;
  32. }
  33. public override void OnRender(ImmediateDrawingContext drawingContext)
  34. {
  35. if (_parameters.Disco > 0.01f)
  36. RegisterForNextAnimationFrameUpdate();
  37. var bounds = GetRenderBounds();
  38. var size = PixelSize.FromSize(bounds.Size, 1);
  39. if (size.Width < 1 || size.Height < 1)
  40. return;
  41. if(drawingContext.TryGetFeature<ISkiaSharpApiLeaseFeature>(out var skiaFeature))
  42. {
  43. using var skiaLease = skiaFeature.Lease();
  44. var grContext = skiaLease.GrContext;
  45. if (grContext == null)
  46. return;
  47. SKImage? snapshot;
  48. using (var platformApiLease = skiaLease.TryLeasePlatformGraphicsApi())
  49. {
  50. if (platformApiLease?.Context is not IGlContext glContext)
  51. return;
  52. var gl = glContext.GlInterface;
  53. if (_gl != glContext)
  54. {
  55. // The old context is lost
  56. _fbo = null;
  57. _contentInitialized = false;
  58. _gl = glContext;
  59. }
  60. gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var oldFb);
  61. _fbo ??= new OpenGlFbo(glContext, grContext);
  62. if (_fbo.Size != size)
  63. _fbo.Resize(size);
  64. gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo.Fbo);
  65. if (!_contentInitialized)
  66. {
  67. _content.Init(gl, glContext.Version);
  68. _contentInitialized = true;
  69. }
  70. _content.OnOpenGlRender(gl, _fbo.Fbo, size, _parameters.Yaw, _parameters.Pitch,
  71. _parameters.Roll, _parameters.Disco);
  72. snapshot = _fbo.Snapshot();
  73. gl.BindFramebuffer(GL_FRAMEBUFFER, oldFb);
  74. }
  75. using(snapshot)
  76. if (snapshot != null)
  77. skiaLease.SkCanvas.DrawImage(snapshot, new SKRect(0, 0,
  78. (float)bounds.Width, (float)bounds.Height));
  79. }
  80. }
  81. public override void OnAnimationFrameUpdate()
  82. {
  83. if (_reRender || _parameters.Disco > 0.01f)
  84. {
  85. _reRender = false;
  86. Invalidate();
  87. }
  88. base.OnAnimationFrameUpdate();
  89. }
  90. public override void OnMessage(object message)
  91. {
  92. if (message is Parameters p)
  93. {
  94. _parameters = p;
  95. _reRender = true;
  96. RegisterForNextAnimationFrameUpdate();
  97. }
  98. else if (message is DisposeMessage)
  99. {
  100. if (_gl != null)
  101. {
  102. try
  103. {
  104. if (_fbo != null || _contentInitialized)
  105. {
  106. using (_gl.MakeCurrent())
  107. {
  108. if (_contentInitialized)
  109. _content.Deinit(_gl.GlInterface);
  110. _contentInitialized = false;
  111. _fbo?.Dispose();
  112. _fbo = null;
  113. }
  114. }
  115. }
  116. catch (Exception e)
  117. {
  118. Console.WriteLine(e.ToString());
  119. }
  120. _gl = null;
  121. }
  122. }
  123. base.OnMessage(message);
  124. }
  125. }
  126. public class Parameters
  127. {
  128. public float Yaw;
  129. public float Pitch;
  130. public float Roll;
  131. public float Disco;
  132. }
  133. public class DisposeMessage
  134. {
  135. }
  136. public OpenGlLeasePage()
  137. {
  138. AvaloniaXamlLoader.Load(this);
  139. _viewport = this.FindControl<Control>("Viewport")!;
  140. _viewport.AttachedToVisualTree += ViewportAttachedToVisualTree;
  141. _viewport.DetachedFromVisualTree += ViewportDetachedFromVisualTree;
  142. _knobs = this.FindControl<GlPageKnobs>("Knobs")!;
  143. _knobs.PropertyChanged += KnobsPropertyChanged;
  144. }
  145. private void KnobsPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs change)
  146. {
  147. if (change.Property == GlPageKnobs.YawProperty
  148. || change.Property == GlPageKnobs.RollProperty
  149. || change.Property == GlPageKnobs.PitchProperty
  150. || change.Property == GlPageKnobs.DiscoProperty)
  151. _visual?.SendHandlerMessage(GetParameters());
  152. }
  153. Parameters GetParameters() => new()
  154. {
  155. Yaw = _knobs!.Yaw, Pitch = _knobs.Pitch, Roll = _knobs.Roll, Disco = _knobs.Disco
  156. };
  157. private void ViewportAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
  158. {
  159. var visual = ElementComposition.GetElementVisual(_viewport!);
  160. if(visual == null)
  161. return;
  162. _visual = visual.Compositor.CreateCustomVisual(new GlVisual(new OpenGlContent(), GetParameters()));
  163. ElementComposition.SetElementChildVisual(_viewport, _visual);
  164. UpdateSize(Bounds.Size);
  165. }
  166. private void UpdateSize(Size size)
  167. {
  168. if (_visual != null)
  169. _visual.Size = new Vector(size.Width, size.Height);
  170. }
  171. protected override Size ArrangeOverride(Size finalSize)
  172. {
  173. var size = base.ArrangeOverride(finalSize);
  174. UpdateSize(size);
  175. return size;
  176. }
  177. private void ViewportDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
  178. {
  179. _visual?.SendHandlerMessage(new DisposeMessage());
  180. _visual = null;
  181. ElementComposition.SetElementChildVisual(_viewport, null);
  182. base.OnDetachedFromVisualTree(e);
  183. }
  184. }