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