SwapchainBase.cs 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reactive.Disposables;
  4. using System.Threading.Tasks;
  5. using Avalonia;
  6. using Avalonia.Rendering.Composition;
  7. namespace Avalonia.Rendering;
  8. /// <summary>
  9. /// A helper class for composition-backed swapchains, should not be a public API yet
  10. /// </summary>
  11. abstract class SwapchainBase<TImage> : IAsyncDisposable where TImage : class, ISwapchainImage
  12. {
  13. protected ICompositionGpuInterop Interop { get; }
  14. protected CompositionDrawingSurface Target { get; }
  15. private List<TImage> _pendingImages = new();
  16. public SwapchainBase(ICompositionGpuInterop interop, CompositionDrawingSurface target)
  17. {
  18. Interop = interop;
  19. Target = target;
  20. }
  21. static bool IsBroken(TImage image) => image.LastPresent?.IsFaulted == true;
  22. static bool IsReady(TImage image) => image.LastPresent == null || image.LastPresent.Status == TaskStatus.RanToCompletion;
  23. TImage? CleanupAndFindNextImage(PixelSize size)
  24. {
  25. TImage? firstFound = null;
  26. var foundMultiple = false;
  27. for (var c = _pendingImages.Count - 1; c > -1; c--)
  28. {
  29. var image = _pendingImages[c];
  30. var ready = IsReady(image);
  31. var matches = image.Size == size;
  32. if (IsBroken(image) || (!matches && ready))
  33. {
  34. image.DisposeAsync();
  35. _pendingImages.RemoveAt(c);
  36. }
  37. if (matches && ready)
  38. {
  39. if (firstFound == null)
  40. firstFound = image;
  41. else
  42. foundMultiple = true;
  43. }
  44. }
  45. // We are making sure that there was at least one image of the same size in flight
  46. // Otherwise we might encounter UI thread lockups
  47. return foundMultiple ? firstFound : null;
  48. }
  49. protected abstract TImage CreateImage(PixelSize size);
  50. protected IDisposable BeginDrawCore(PixelSize size, out TImage image)
  51. {
  52. var img = CleanupAndFindNextImage(size) ?? CreateImage(size);
  53. img.BeginDraw();
  54. _pendingImages.Remove(img);
  55. image = img;
  56. return Disposable.Create(() =>
  57. {
  58. img.Present();
  59. _pendingImages.Add(img);
  60. });
  61. }
  62. public async ValueTask DisposeAsync()
  63. {
  64. foreach (var img in _pendingImages)
  65. await img.DisposeAsync();
  66. }
  67. }
  68. interface ISwapchainImage : IAsyncDisposable
  69. {
  70. PixelSize Size { get; }
  71. Task? LastPresent { get; }
  72. void BeginDraw();
  73. void Present();
  74. }