mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Software rendering - Fixed memory leak
Rendering - Swap back and forth between software and GPU without crash
This commit is contained in:
parent
97668ee932
commit
c444ff4e59
@ -9,6 +9,7 @@ namespace Artemis.Core
|
||||
private bool _disposed;
|
||||
private SKRect _lastBounds;
|
||||
private SKRect _lastParentBounds;
|
||||
private GRContext? _lastGraphicsContext;
|
||||
public SKSurface? Surface { get; private set; }
|
||||
public SKPaint? Paint { get; private set; }
|
||||
public SKPath? Path { get; private set; }
|
||||
@ -27,7 +28,7 @@ namespace Artemis.Core
|
||||
if (IsOpen)
|
||||
throw new ArtemisCoreException("Cannot open render context because it is already open");
|
||||
|
||||
if (path.Bounds != _lastBounds || (parent != null && parent.Bounds != _lastParentBounds))
|
||||
if (path.Bounds != _lastBounds || (parent != null && parent.Bounds != _lastParentBounds) || _lastGraphicsContext != Constants.ManagedGraphicsContext?.GraphicsContext)
|
||||
Invalidate();
|
||||
|
||||
if (!_valid || Surface == null)
|
||||
@ -41,7 +42,7 @@ namespace Artemis.Core
|
||||
Surface = SKSurface.Create(imageInfo);
|
||||
else
|
||||
Surface = SKSurface.Create(Constants.ManagedGraphicsContext.GraphicsContext, true, imageInfo);
|
||||
|
||||
|
||||
Path = new SKPath(path);
|
||||
Path.Transform(SKMatrix.CreateTranslation(pathBounds.Left * -1, pathBounds.Top * -1));
|
||||
|
||||
@ -53,6 +54,7 @@ namespace Artemis.Core
|
||||
|
||||
_lastParentBounds = parent?.Bounds ?? new SKRect();
|
||||
_lastBounds = path.Bounds;
|
||||
_lastGraphicsContext = Constants.ManagedGraphicsContext?.GraphicsContext;
|
||||
_valid = true;
|
||||
}
|
||||
|
||||
@ -70,7 +72,14 @@ namespace Artemis.Core
|
||||
throw new ObjectDisposedException("Renderer");
|
||||
|
||||
Surface?.Canvas.Restore();
|
||||
|
||||
// Looks like every part of the paint needs to be disposed :(
|
||||
Paint?.ColorFilter?.Dispose();
|
||||
Paint?.ImageFilter?.Dispose();
|
||||
Paint?.MaskFilter?.Dispose();
|
||||
Paint?.PathEffect?.Dispose();
|
||||
Paint?.Dispose();
|
||||
|
||||
Paint = null;
|
||||
|
||||
IsOpen = false;
|
||||
|
||||
@ -51,6 +51,8 @@ namespace Artemis.Core.Services
|
||||
|
||||
UpdateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / _targetFrameRateSetting.Value};
|
||||
Surface.RegisterUpdateTrigger(UpdateTrigger);
|
||||
|
||||
Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
|
||||
}
|
||||
|
||||
public TimerUpdateTrigger UpdateTrigger { get; }
|
||||
@ -66,6 +68,11 @@ namespace Artemis.Core.Services
|
||||
_texture?.Invalidate();
|
||||
}
|
||||
|
||||
private void UtilitiesOnShutdownRequested(object? sender, EventArgs e)
|
||||
{
|
||||
IsRenderPaused = true;
|
||||
}
|
||||
|
||||
private void SurfaceOnLayoutChanged(SurfaceLayoutChangedEventArgs args)
|
||||
{
|
||||
UpdateLedGroup();
|
||||
@ -115,6 +122,11 @@ namespace Artemis.Core.Services
|
||||
DeviceAdded?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e)
|
||||
{
|
||||
_texture?.Invalidate();
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<ArtemisDevice> EnabledDevices => _enabledDevices.AsReadOnly();
|
||||
public IReadOnlyCollection<ArtemisDevice> Devices => _devices.AsReadOnly();
|
||||
public IReadOnlyDictionary<Led, ArtemisLed> LedMap => new ReadOnlyDictionary<Led, ArtemisLed>(_ledMap);
|
||||
@ -195,11 +207,6 @@ namespace Artemis.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderScaleSettingOnSettingChanged(object? sender, EventArgs e)
|
||||
{
|
||||
_texture?.Invalidate();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Surface.UnregisterUpdateTrigger(UpdateTrigger);
|
||||
@ -245,7 +252,7 @@ namespace Artemis.Core.Services
|
||||
IManagedGraphicsContext? graphicsContext = Constants.ManagedGraphicsContext = _newGraphicsContext;
|
||||
if (!ReferenceEquals(graphicsContext, _newGraphicsContext))
|
||||
graphicsContext = _newGraphicsContext;
|
||||
|
||||
|
||||
if (graphicsContext != null)
|
||||
_logger.Debug("Creating SKTexture with graphics context {graphicsContext}", graphicsContext.GetType().Name);
|
||||
else
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"profiles": {
|
||||
"Artemis.UI": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "--force-elevation"
|
||||
"commandLineArgs": "--force-elevation --pcmr"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -32,11 +32,9 @@
|
||||
<TextBlock Grid.Column="1" HorizontalAlignment="Right" Margin="0,0,5,0">
|
||||
<Run Text="FPS: " />
|
||||
<Run FontWeight="Bold" Text="{Binding CurrentFps}" />
|
||||
<Run Text=" at " />
|
||||
<Run Text="{Binding RenderWidth}" /><Run Text="x" />
|
||||
<Run Text="{Binding RenderHeight}" />
|
||||
|
||||
<Run Text=" - Renderer: "></Run>
|
||||
<Run Text="at" />
|
||||
<Run Text="{Binding RenderWidth}" /><Run Text="x" /><Run Text="{Binding RenderHeight}" />
|
||||
<Run Text="- Renderer:"></Run>
|
||||
<Run Text="{Binding Renderer}"></Run>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
|
||||
@ -21,6 +21,8 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
|
||||
private int _renderHeight;
|
||||
private string _frameTargetPath;
|
||||
private string _renderer;
|
||||
private int _frames;
|
||||
private DateTime _frameCountStart;
|
||||
|
||||
public RenderDebugViewModel(ICoreService coreService)
|
||||
{
|
||||
@ -76,8 +78,6 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
|
||||
{
|
||||
_coreService.FrameRendered += CoreServiceOnFrameRendered;
|
||||
_coreService.FrameRendering += CoreServiceOnFrameRendering;
|
||||
|
||||
Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
|
||||
base.OnActivate();
|
||||
}
|
||||
|
||||
@ -132,7 +132,15 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
|
||||
|
||||
private void CoreServiceOnFrameRendering(object sender, FrameRenderingEventArgs e)
|
||||
{
|
||||
CurrentFps = Math.Round(1.0 / e.DeltaTime, 2);
|
||||
if (DateTime.Now - _frameCountStart >= TimeSpan.FromSeconds(1))
|
||||
{
|
||||
CurrentFps = _frames;
|
||||
Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
|
||||
|
||||
_frames = 0;
|
||||
_frameCountStart = DateTime.Now;
|
||||
}
|
||||
_frames++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -28,6 +28,7 @@ namespace Artemis.UI.Services
|
||||
private bool _registeredBuiltInDataModelDisplays;
|
||||
private bool _registeredBuiltInDataModelInputs;
|
||||
private bool _registeredBuiltInPropertyEditors;
|
||||
private VulkanContext _vulkanContext;
|
||||
|
||||
public RegistrationService(ILogger logger,
|
||||
ICoreService coreService,
|
||||
@ -128,7 +129,8 @@ namespace Artemis.UI.Services
|
||||
_rgbService.UpdateGraphicsContext(null);
|
||||
break;
|
||||
case "Vulkan":
|
||||
_rgbService.UpdateGraphicsContext(new VulkanContext());
|
||||
_vulkanContext ??= new VulkanContext();
|
||||
_rgbService.UpdateGraphicsContext(_vulkanContext);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
@ -8,76 +8,79 @@ namespace Artemis.UI.SkiaSharp.Vulkan
|
||||
{
|
||||
internal sealed class Win32VkContext : VkContext
|
||||
{
|
||||
private static readonly NativeWindow window = new NativeWindow();
|
||||
public NativeWindow Window { get; }
|
||||
|
||||
public Win32VkContext()
|
||||
{
|
||||
Instance = Instance.Create(null, new[] { "VK_KHR_surface", "VK_KHR_win32_surface" });
|
||||
public Win32VkContext()
|
||||
{
|
||||
Window = new NativeWindow();
|
||||
Instance = Instance.Create(null, new[] {"VK_KHR_surface", "VK_KHR_win32_surface"});
|
||||
PhysicalDevice = Instance.EnumeratePhysicalDevices().First();
|
||||
Surface = Instance.CreateWin32Surface(Kernel32.CurrentModuleHandle, Window.Handle);
|
||||
|
||||
PhysicalDevice = Instance.EnumeratePhysicalDevices().First();
|
||||
(GraphicsFamily, PresentFamily) = FindQueueFamilies();
|
||||
|
||||
Surface = Instance.CreateWin32Surface(Kernel32.CurrentModuleHandle, window.Handle);
|
||||
DeviceQueueCreateInfo[] queueInfos =
|
||||
{
|
||||
new() {QueueFamilyIndex = GraphicsFamily, QueuePriorities = new[] {1f}},
|
||||
new() {QueueFamilyIndex = PresentFamily, QueuePriorities = new[] {1f}}
|
||||
};
|
||||
Device = PhysicalDevice.CreateDevice(queueInfos, null, null);
|
||||
GraphicsQueue = Device.GetQueue(GraphicsFamily, 0);
|
||||
PresentQueue = Device.GetQueue(PresentFamily, 0);
|
||||
|
||||
(GraphicsFamily, PresentFamily) = FindQueueFamilies();
|
||||
GetProc = (name, instanceHandle, deviceHandle) =>
|
||||
{
|
||||
if (deviceHandle != IntPtr.Zero)
|
||||
return Device.GetProcedureAddress(name);
|
||||
|
||||
DeviceQueueCreateInfo[]? queueInfos = new[]
|
||||
{
|
||||
new DeviceQueueCreateInfo { QueueFamilyIndex = GraphicsFamily, QueuePriorities = new[] { 1f } },
|
||||
new DeviceQueueCreateInfo { QueueFamilyIndex = PresentFamily, QueuePriorities = new[] { 1f } },
|
||||
};
|
||||
Device = PhysicalDevice.CreateDevice(queueInfos, null, null);
|
||||
return Instance.GetProcedureAddress(name);
|
||||
};
|
||||
|
||||
GraphicsQueue = Device.GetQueue(GraphicsFamily, 0);
|
||||
SharpVkGetProc = (name, instance, device) =>
|
||||
{
|
||||
if (device != null)
|
||||
return device.GetProcedureAddress(name);
|
||||
if (instance != null)
|
||||
return instance.GetProcedureAddress(name);
|
||||
|
||||
PresentQueue = Device.GetQueue(PresentFamily, 0);
|
||||
// SharpVk includes the static functions on Instance, but this is not actually correct
|
||||
// since the functions are static, they are not tied to an instance. For example,
|
||||
// VkCreateInstance is not found on an instance, it is creating said instance.
|
||||
// Other libraries, such as VulkanCore, use another type to do this.
|
||||
return Instance.GetProcedureAddress(name);
|
||||
};
|
||||
}
|
||||
|
||||
GetProc = (name, instanceHandle, deviceHandle) =>
|
||||
{
|
||||
if (deviceHandle != IntPtr.Zero)
|
||||
return Device.GetProcedureAddress(name);
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
Window.DestroyHandle();
|
||||
}
|
||||
|
||||
return Instance.GetProcedureAddress(name);
|
||||
};
|
||||
private (uint, uint) FindQueueFamilies()
|
||||
{
|
||||
QueueFamilyProperties[] queueFamilyProperties = PhysicalDevice.GetQueueFamilyProperties();
|
||||
|
||||
SharpVkGetProc = (name, instance, device) =>
|
||||
{
|
||||
if (device != null)
|
||||
return device.GetProcedureAddress(name);
|
||||
if (instance != null)
|
||||
return instance.GetProcedureAddress(name);
|
||||
var graphicsFamily = queueFamilyProperties
|
||||
.Select((properties, index) => new {properties, index})
|
||||
.SkipWhile(pair => !pair.properties.QueueFlags.HasFlag(QueueFlags.Graphics))
|
||||
.FirstOrDefault();
|
||||
|
||||
// SharpVk includes the static functions on Instance, but this is not actually correct
|
||||
// since the functions are static, they are not tied to an instance. For example,
|
||||
// VkCreateInstance is not found on an instance, it is creating said instance.
|
||||
// Other libraries, such as VulkanCore, use another type to do this.
|
||||
return Instance.GetProcedureAddress(name);
|
||||
};
|
||||
}
|
||||
if (graphicsFamily == null)
|
||||
throw new Exception("Unable to find graphics queue");
|
||||
|
||||
private (uint, uint) FindQueueFamilies()
|
||||
{
|
||||
QueueFamilyProperties[]? queueFamilyProperties = PhysicalDevice.GetQueueFamilyProperties();
|
||||
uint? presentFamily = default;
|
||||
|
||||
var graphicsFamily = queueFamilyProperties
|
||||
.Select((properties, index) => new { properties, index })
|
||||
.SkipWhile(pair => !pair.properties.QueueFlags.HasFlag(QueueFlags.Graphics))
|
||||
.FirstOrDefault();
|
||||
for (uint i = 0; i < queueFamilyProperties.Length; ++i)
|
||||
{
|
||||
if (PhysicalDevice.GetSurfaceSupport(i, Surface))
|
||||
presentFamily = i;
|
||||
}
|
||||
|
||||
if (graphicsFamily == null)
|
||||
throw new Exception("Unable to find graphics queue");
|
||||
if (!presentFamily.HasValue)
|
||||
throw new Exception("Unable to find present queue");
|
||||
|
||||
uint? presentFamily = default;
|
||||
|
||||
for (uint i = 0; i < queueFamilyProperties.Length; ++i)
|
||||
{
|
||||
if (PhysicalDevice.GetSurfaceSupport(i, Surface))
|
||||
presentFamily = i;
|
||||
}
|
||||
|
||||
if (!presentFamily.HasValue)
|
||||
throw new Exception("Unable to find present queue");
|
||||
|
||||
return ((uint)graphicsFamily.index, presentFamily.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ((uint) graphicsFamily.index, presentFamily.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -50,14 +50,13 @@ namespace Artemis.UI.SkiaSharp
|
||||
{
|
||||
throw new ArtemisGraphicsContextException("Failed to create Vulkan graphics context", e);
|
||||
}
|
||||
|
||||
GraphicsContext.Flush();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_vulkanBackendContext?.Dispose();
|
||||
_vulkanContext?.Dispose();
|
||||
GraphicsContext?.Dispose();
|
||||
}
|
||||
|
||||
public GRContext GraphicsContext { get; }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user