mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Core - Reworked rendering pipeline to work with different contexts
Core - Added GPU-based Vulkan rendering context
This commit is contained in:
parent
34b9d69ed8
commit
a02431bb82
@ -4,6 +4,7 @@ using System.IO;
|
|||||||
using Artemis.Core.JsonConverters;
|
using Artemis.Core.JsonConverters;
|
||||||
using Artemis.Core.Services.Core;
|
using Artemis.Core.Services.Core;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
@ -116,5 +117,21 @@ namespace Artemis.Core
|
|||||||
typeof(double),
|
typeof(double),
|
||||||
typeof(decimal)
|
typeof(decimal)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static GRContext? _skiaGraphicsContext;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the graphics context to be used for rendering by SkiaSharp
|
||||||
|
/// </summary>
|
||||||
|
public static GRContext? SkiaGraphicsContext
|
||||||
|
{
|
||||||
|
get => _skiaGraphicsContext;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_skiaGraphicsContext != null)
|
||||||
|
throw new ArtemisCoreException($"{nameof(SkiaGraphicsContext)} can only be set once.");
|
||||||
|
_skiaGraphicsContext = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,12 +193,12 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
canvas.Save();
|
canvas.Save();
|
||||||
Renderer.Open(Path, Parent as Folder);
|
Renderer.Open(Path, Parent as Folder);
|
||||||
if (Renderer.Canvas == null || Renderer.Path == null || Renderer.Paint == null)
|
if (Renderer.Surface == null || Renderer.Path == null || Renderer.Paint == null)
|
||||||
throw new ArtemisCoreException("Failed to open folder render context");
|
throw new ArtemisCoreException("Failed to open folder render context");
|
||||||
|
|
||||||
SKRect rendererBounds = Renderer.Path.Bounds;
|
SKRect rendererBounds = Renderer.Path.Bounds;
|
||||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
baseLayerEffect.PreProcess(Renderer.Canvas, rendererBounds, Renderer.Paint);
|
baseLayerEffect.PreProcess(Renderer.Surface.Canvas, rendererBounds, Renderer.Paint);
|
||||||
|
|
||||||
// If required, apply the opacity override of the module to the root folder
|
// If required, apply the opacity override of the module to the root folder
|
||||||
if (IsRootFolder && Profile.Module.OpacityOverride < 1)
|
if (IsRootFolder && Profile.Module.OpacityOverride < 1)
|
||||||
@ -213,12 +213,12 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
// Iterate the children in reverse because the first layer must be rendered last to end up on top
|
// Iterate the children in reverse because the first layer must be rendered last to end up on top
|
||||||
for (int index = Children.Count - 1; index > -1; index--)
|
for (int index = Children.Count - 1; index > -1; index--)
|
||||||
Children[index].Render(Renderer.Canvas);
|
Children[index].Render(Renderer.Surface.Canvas);
|
||||||
|
|
||||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
baseLayerEffect.PostProcess(Renderer.Canvas, rendererBounds, Renderer.Paint);
|
baseLayerEffect.PostProcess(Renderer.Surface.Canvas, rendererBounds, Renderer.Paint);
|
||||||
|
|
||||||
canvas.DrawBitmap(Renderer.Bitmap, Renderer.TargetLocation, Renderer.Paint);
|
canvas.DrawSurface(Renderer.Surface, Renderer.TargetLocation, Renderer.Paint);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@ -330,8 +330,7 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
canvas.Save();
|
canvas.Save();
|
||||||
Renderer.Open(Path, Parent as Folder);
|
Renderer.Open(Path, Parent as Folder);
|
||||||
|
if (Renderer.Surface == null || Renderer.Path == null || Renderer.Paint == null)
|
||||||
if (Renderer.Canvas == null || Renderer.Path == null || Renderer.Paint == null)
|
|
||||||
throw new ArtemisCoreException("Failed to open layer render context");
|
throw new ArtemisCoreException("Failed to open layer render context");
|
||||||
|
|
||||||
// Apply blend mode and color
|
// Apply blend mode and color
|
||||||
@ -357,11 +356,11 @@ namespace Artemis.Core
|
|||||||
if (LayerBrush.SupportsTransformation)
|
if (LayerBrush.SupportsTransformation)
|
||||||
{
|
{
|
||||||
SKMatrix rotationMatrix = GetTransformMatrix(true, false, false, true);
|
SKMatrix rotationMatrix = GetTransformMatrix(true, false, false, true);
|
||||||
Renderer.Canvas.SetMatrix(Renderer.Canvas.TotalMatrix.PreConcat(rotationMatrix));
|
Renderer.Surface.Canvas.SetMatrix(Renderer.Surface.Canvas.TotalMatrix.PreConcat(rotationMatrix));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
|
// If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
|
||||||
Renderer.Canvas.ClipPath(renderPath);
|
Renderer.Surface.Canvas.ClipPath(renderPath);
|
||||||
DelegateRendering(renderPath.Bounds);
|
DelegateRendering(renderPath.Bounds);
|
||||||
}
|
}
|
||||||
else if (General.TransformMode.CurrentValue == LayerTransformMode.Clip)
|
else if (General.TransformMode.CurrentValue == LayerTransformMode.Clip)
|
||||||
@ -370,11 +369,11 @@ namespace Artemis.Core
|
|||||||
renderPath.Transform(renderPathMatrix);
|
renderPath.Transform(renderPathMatrix);
|
||||||
|
|
||||||
// If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
|
// If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
|
||||||
Renderer.Canvas.ClipPath(renderPath);
|
Renderer.Surface.Canvas.ClipPath(renderPath);
|
||||||
DelegateRendering(Renderer.Path.Bounds);
|
DelegateRendering(Renderer.Path.Bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.DrawBitmap(Renderer.Bitmap, Renderer.TargetLocation, Renderer.Paint);
|
canvas.DrawSurface(Renderer.Surface, Renderer.TargetLocation, Renderer.Paint);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -395,16 +394,16 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
if (LayerBrush == null)
|
if (LayerBrush == null)
|
||||||
throw new ArtemisCoreException("The layer is not yet ready for rendering");
|
throw new ArtemisCoreException("The layer is not yet ready for rendering");
|
||||||
if (Renderer.Canvas == null || Renderer.Paint == null)
|
if (Renderer.Surface == null || Renderer.Paint == null)
|
||||||
throw new ArtemisCoreException("Failed to open layer render context");
|
throw new ArtemisCoreException("Failed to open layer render context");
|
||||||
|
|
||||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
baseLayerEffect.PreProcess(Renderer.Canvas, bounds, Renderer.Paint);
|
baseLayerEffect.PreProcess(Renderer.Surface.Canvas, bounds, Renderer.Paint);
|
||||||
|
|
||||||
LayerBrush.InternalRender(Renderer.Canvas, bounds, Renderer.Paint);
|
LayerBrush.InternalRender(Renderer.Surface.Canvas, bounds, Renderer.Paint);
|
||||||
|
|
||||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
baseLayerEffect.PostProcess(Renderer.Canvas, bounds, Renderer.Paint);
|
baseLayerEffect.PostProcess(Renderer.Surface.Canvas, bounds, Renderer.Paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void CalculateRenderProperties()
|
internal void CalculateRenderProperties()
|
||||||
|
|||||||
@ -9,8 +9,7 @@ namespace Artemis.Core
|
|||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
private SKRect _lastBounds;
|
private SKRect _lastBounds;
|
||||||
private SKRect _lastParentBounds;
|
private SKRect _lastParentBounds;
|
||||||
public SKBitmap? Bitmap { get; private set; }
|
public SKSurface? Surface { get; private set; }
|
||||||
public SKCanvas? Canvas { 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; }
|
||||||
public SKPoint TargetLocation { get; private set; }
|
public SKPoint TargetLocation { get; private set; }
|
||||||
@ -31,22 +30,26 @@ namespace Artemis.Core
|
|||||||
if (path.Bounds != _lastBounds || (parent != null && parent.Bounds != _lastParentBounds))
|
if (path.Bounds != _lastBounds || (parent != null && parent.Bounds != _lastParentBounds))
|
||||||
Invalidate();
|
Invalidate();
|
||||||
|
|
||||||
if (!_valid || Canvas == null)
|
if (!_valid || Surface == null)
|
||||||
{
|
{
|
||||||
SKRect pathBounds = path.Bounds;
|
SKRect pathBounds = path.Bounds;
|
||||||
int width = (int) pathBounds.Width;
|
int width = (int) pathBounds.Width;
|
||||||
int height = (int) pathBounds.Height;
|
int height = (int) pathBounds.Height;
|
||||||
|
|
||||||
Bitmap = new SKBitmap(width, height);
|
SKImageInfo imageInfo = new SKImageInfo(width, height);
|
||||||
|
if (Constants.SkiaGraphicsContext == null)
|
||||||
|
Surface = SKSurface.Create(imageInfo);
|
||||||
|
else
|
||||||
|
Surface = SKSurface.Create(Constants.SkiaGraphicsContext, true, imageInfo);
|
||||||
|
|
||||||
Path = new SKPath(path);
|
Path = new SKPath(path);
|
||||||
Canvas = new SKCanvas(Bitmap);
|
|
||||||
Path.Transform(SKMatrix.CreateTranslation(pathBounds.Left * -1, pathBounds.Top * -1));
|
Path.Transform(SKMatrix.CreateTranslation(pathBounds.Left * -1, pathBounds.Top * -1));
|
||||||
|
|
||||||
TargetLocation = new SKPoint(pathBounds.Location.X, pathBounds.Location.Y);
|
TargetLocation = new SKPoint(pathBounds.Location.X, pathBounds.Location.Y);
|
||||||
if (parent != null)
|
if (parent != null)
|
||||||
TargetLocation -= parent.Bounds.Location;
|
TargetLocation -= parent.Bounds.Location;
|
||||||
|
|
||||||
Canvas.ClipPath(Path);
|
Surface.Canvas.ClipPath(Path);
|
||||||
|
|
||||||
_lastParentBounds = parent?.Bounds ?? new SKRect();
|
_lastParentBounds = parent?.Bounds ?? new SKRect();
|
||||||
_lastBounds = path.Bounds;
|
_lastBounds = path.Bounds;
|
||||||
@ -55,8 +58,8 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
Paint = new SKPaint();
|
Paint = new SKPaint();
|
||||||
|
|
||||||
Canvas.Clear();
|
Surface.Canvas.Clear();
|
||||||
Canvas.Save();
|
Surface.Canvas.Save();
|
||||||
|
|
||||||
IsOpen = true;
|
IsOpen = true;
|
||||||
}
|
}
|
||||||
@ -66,7 +69,7 @@ namespace Artemis.Core
|
|||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("Renderer");
|
throw new ObjectDisposedException("Renderer");
|
||||||
|
|
||||||
Canvas?.Restore();
|
Surface?.Canvas.Restore();
|
||||||
Paint?.Dispose();
|
Paint?.Dispose();
|
||||||
Paint = null;
|
Paint = null;
|
||||||
|
|
||||||
@ -86,15 +89,13 @@ namespace Artemis.Core
|
|||||||
if (IsOpen)
|
if (IsOpen)
|
||||||
Close();
|
Close();
|
||||||
|
|
||||||
Canvas?.Dispose();
|
Surface?.Dispose();
|
||||||
Paint?.Dispose();
|
Paint?.Dispose();
|
||||||
Path?.Dispose();
|
Path?.Dispose();
|
||||||
Bitmap?.Dispose();
|
|
||||||
|
|
||||||
Canvas = null;
|
Surface = null;
|
||||||
Paint = null;
|
Paint = null;
|
||||||
Path = null;
|
Path = null;
|
||||||
Bitmap = null;
|
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using RGB.NET.Core;
|
using RGB.NET.Core;
|
||||||
using RGB.NET.Presets.Textures.Sampler;
|
using RGB.NET.Presets.Textures.Sampler;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
@ -10,14 +12,19 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class SKTexture : PixelTexture<byte>, IDisposable
|
public sealed class SKTexture : PixelTexture<byte>, IDisposable
|
||||||
{
|
{
|
||||||
private bool _disposed;
|
private SKPixmap? _pixelData;
|
||||||
|
private SKImage? _rasterImage;
|
||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
internal SKTexture(int width, int height, float renderScale)
|
internal SKTexture(int width, int height, float renderScale)
|
||||||
: base(width, height, 4, new AverageByteSampler())
|
: base(width, height, 4, new AverageByteSampler())
|
||||||
{
|
{
|
||||||
Bitmap = new SKBitmap(new SKImageInfo(width, height, SKColorType.Rgb888x));
|
ImageInfo = new SKImageInfo(width, height);
|
||||||
|
if (Constants.SkiaGraphicsContext == null)
|
||||||
|
Surface = SKSurface.Create(ImageInfo);
|
||||||
|
else
|
||||||
|
Surface = SKSurface.Create(Constants.SkiaGraphicsContext, true, ImageInfo);
|
||||||
RenderScale = renderScale;
|
RenderScale = renderScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,10 +32,20 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
|
internal void CopyPixelData()
|
||||||
|
{
|
||||||
|
using SKImage skImage = Surface.Snapshot();
|
||||||
|
|
||||||
|
_rasterImage?.Dispose();
|
||||||
|
_pixelData?.Dispose();
|
||||||
|
_rasterImage = skImage.ToRasterImage();
|
||||||
|
_pixelData = _rasterImage.PeekPixels();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override Color GetColor(in ReadOnlySpan<byte> pixel)
|
protected override Color GetColor(in ReadOnlySpan<byte> pixel)
|
||||||
{
|
{
|
||||||
return new(pixel[0], pixel[1], pixel[2]);
|
return new(pixel[2], pixel[1], pixel[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -38,12 +55,17 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the SKBitmap backing this texture
|
/// Gets the SKBitmap backing this texture
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SKBitmap Bitmap { get; }
|
public SKSurface Surface { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the image info used to create the <see cref="Surface" />
|
||||||
|
/// </summary>
|
||||||
|
public SKImageInfo ImageInfo { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the color data in RGB format
|
/// Gets the color data in RGB format
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override ReadOnlySpan<byte> Data => _disposed ? new ReadOnlySpan<byte>() : Bitmap.GetPixelSpan();
|
protected override ReadOnlySpan<byte> Data => _pixelData != null ? _pixelData.GetPixelSpan() : ReadOnlySpan<byte>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the render scale of the texture
|
/// Gets the render scale of the texture
|
||||||
@ -67,8 +89,8 @@ namespace Artemis.Core
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_disposed = true;
|
Surface.Dispose();
|
||||||
Bitmap.Dispose();
|
_pixelData?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -139,22 +139,24 @@ namespace Artemis.Core.Services
|
|||||||
module.InternalUpdate(args.DeltaTime);
|
module.InternalUpdate(args.DeltaTime);
|
||||||
|
|
||||||
// Render all active modules
|
// Render all active modules
|
||||||
SKTexture texture =_rgbService.OpenRender();
|
SKTexture texture = _rgbService.OpenRender();
|
||||||
|
|
||||||
using (SKCanvas canvas = new(texture.Bitmap))
|
SKCanvas canvas = texture.Surface.Canvas;
|
||||||
|
canvas.Save();
|
||||||
|
canvas.Scale(texture.RenderScale);
|
||||||
|
canvas.Clear(new SKColor(0, 0, 0));
|
||||||
|
|
||||||
|
// While non-activated modules may be updated above if they expand the main data model, they may never render
|
||||||
|
if (!ModuleRenderingDisabled)
|
||||||
{
|
{
|
||||||
canvas.Scale(texture.RenderScale);
|
foreach (Module module in modules.Where(m => m.IsActivated))
|
||||||
canvas.Clear(new SKColor(0, 0, 0));
|
module.InternalRender(args.DeltaTime, canvas, texture.ImageInfo);
|
||||||
// While non-activated modules may be updated above if they expand the main data model, they may never render
|
|
||||||
if (!ModuleRenderingDisabled)
|
|
||||||
{
|
|
||||||
foreach (Module module in modules.Where(m => m.IsActivated))
|
|
||||||
module.InternalRender(args.DeltaTime, canvas, texture.Bitmap.Info);
|
|
||||||
}
|
|
||||||
|
|
||||||
OnFrameRendering(new FrameRenderingEventArgs(canvas, args.DeltaTime, _rgbService.Surface));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnFrameRendering(new FrameRenderingEventArgs(canvas, args.DeltaTime, _rgbService.Surface));
|
||||||
|
canvas.RestoreToCount(-1);
|
||||||
|
canvas.Flush();
|
||||||
|
|
||||||
OnFrameRendered(new FrameRenderedEventArgs(texture, _rgbService.Surface));
|
OnFrameRendered(new FrameRenderedEventArgs(texture, _rgbService.Surface));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@ -8,7 +8,6 @@ using Artemis.Storage.Entities.Surface;
|
|||||||
using Artemis.Storage.Repositories.Interfaces;
|
using Artemis.Storage.Repositories.Interfaces;
|
||||||
using RGB.NET.Core;
|
using RGB.NET.Core;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Services
|
namespace Artemis.Core.Services
|
||||||
{
|
{
|
||||||
@ -234,6 +233,7 @@ namespace Artemis.Core.Services
|
|||||||
throw new ArtemisCoreException("Render pipeline is already closed");
|
throw new ArtemisCoreException("Render pipeline is already closed");
|
||||||
|
|
||||||
RenderOpen = false;
|
RenderOpen = false;
|
||||||
|
_texture?.CopyPixelData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CreateTexture()
|
public void CreateTexture()
|
||||||
|
|||||||
@ -149,6 +149,7 @@
|
|||||||
<PackageReference Include="RawInput.Sharp" Version="0.0.3" />
|
<PackageReference Include="RawInput.Sharp" Version="0.0.3" />
|
||||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||||
<PackageReference Include="SkiaSharp.Views.WPF" Version="2.80.2" />
|
<PackageReference Include="SkiaSharp.Views.WPF" Version="2.80.2" />
|
||||||
|
<PackageReference Include="SkiaSharp.Vulkan.SharpVk" Version="2.80.2" />
|
||||||
<PackageReference Include="Stylet" Version="1.3.5" />
|
<PackageReference Include="Stylet" Version="1.3.5" />
|
||||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
||||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||||
|
|||||||
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Markup;
|
using System.Windows.Markup;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
|
using Artemis.Core;
|
||||||
using Artemis.Core.Ninject;
|
using Artemis.Core.Ninject;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Ninject;
|
using Artemis.UI.Ninject;
|
||||||
@ -13,9 +14,11 @@ using Artemis.UI.Screens;
|
|||||||
using Artemis.UI.Services;
|
using Artemis.UI.Services;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
|
using Artemis.UI.SkiaSharp;
|
||||||
using Artemis.UI.Stylet;
|
using Artemis.UI.Stylet;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
using SkiaSharp;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
|
|
||||||
namespace Artemis.UI
|
namespace Artemis.UI
|
||||||
@ -69,6 +72,8 @@ namespace Artemis.UI
|
|||||||
{
|
{
|
||||||
UIElement view = viewManager.CreateAndBindViewForModelIfNecessary(RootViewModel);
|
UIElement view = viewManager.CreateAndBindViewForModelIfNecessary(RootViewModel);
|
||||||
((TrayViewModel) RootViewModel).SetTaskbarIcon(view);
|
((TrayViewModel) RootViewModel).SetTaskbarIcon(view);
|
||||||
|
|
||||||
|
CreateGraphicsContext();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize the core async so the UI can show the progress
|
// Initialize the core async so the UI can show the progress
|
||||||
@ -128,6 +133,22 @@ namespace Artemis.UI
|
|||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CreateGraphicsContext()
|
||||||
|
{
|
||||||
|
Win32VkContext vulkanContext = new();
|
||||||
|
GRVkBackendContext vulkanBackendContext = new()
|
||||||
|
{
|
||||||
|
VkInstance = (IntPtr) vulkanContext.Instance.RawHandle.ToUInt64(),
|
||||||
|
VkPhysicalDevice = (IntPtr) vulkanContext.PhysicalDevice.RawHandle.ToUInt64(),
|
||||||
|
VkDevice = (IntPtr) vulkanContext.Device.RawHandle.ToUInt64(),
|
||||||
|
VkQueue = (IntPtr) vulkanContext.GraphicsQueue.RawHandle.ToUInt64(),
|
||||||
|
GraphicsQueueIndex = vulkanContext.GraphicsFamily,
|
||||||
|
GetProcedureAddress = vulkanContext.GetProc
|
||||||
|
};
|
||||||
|
|
||||||
|
Constants.SkiaGraphicsContext = GRContext.CreateVulkan(vulkanBackendContext);
|
||||||
|
}
|
||||||
|
|
||||||
private void HandleFatalException(Exception e, ILogger logger)
|
private void HandleFatalException(Exception e, ILogger logger)
|
||||||
{
|
{
|
||||||
logger.Fatal(e, "Fatal exception during initialization, shutting down.");
|
logger.Fatal(e, "Fatal exception during initialization, shutting down.");
|
||||||
|
|||||||
@ -83,18 +83,20 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
|
|||||||
{
|
{
|
||||||
Execute.OnUIThreadSync(() =>
|
Execute.OnUIThreadSync(() =>
|
||||||
{
|
{
|
||||||
SKImageInfo bitmapInfo = e.Texture.Bitmap.Info;
|
SKImageInfo bitmapInfo = e.Texture.ImageInfo;
|
||||||
RenderHeight = bitmapInfo.Height;
|
RenderHeight = bitmapInfo.Height;
|
||||||
RenderWidth = bitmapInfo.Width;
|
RenderWidth = bitmapInfo.Width;
|
||||||
|
|
||||||
// ReSharper disable twice CompareOfFloatsByEqualityOperator
|
// ReSharper disable twice CompareOfFloatsByEqualityOperator
|
||||||
|
|
||||||
|
|
||||||
if (CurrentFrame is not WriteableBitmap writable || writable.Width != bitmapInfo.Width || writable.Height != bitmapInfo.Height)
|
if (CurrentFrame is not WriteableBitmap writable || writable.Width != bitmapInfo.Width || writable.Height != bitmapInfo.Height)
|
||||||
{
|
{
|
||||||
CurrentFrame = e.Texture.Bitmap.ToWriteableBitmap();
|
CurrentFrame = e.Texture.Surface.Snapshot().ToWriteableBitmap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using SKImage skImage = SKImage.FromPixels(e.Texture.Bitmap.PeekPixels());
|
using SKImage skImage = e.Texture.Surface.Snapshot();
|
||||||
|
|
||||||
if (_frameTargetPath != null)
|
if (_frameTargetPath != null)
|
||||||
{
|
{
|
||||||
|
|||||||
24
src/Artemis.UI/SkiaSharp/Kernel32.cs
Normal file
24
src/Artemis.UI/SkiaSharp/Kernel32.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Artemis.UI.SkiaSharp
|
||||||
|
{
|
||||||
|
internal class Kernel32
|
||||||
|
{
|
||||||
|
private const string kernel32 = "kernel32.dll";
|
||||||
|
|
||||||
|
static Kernel32()
|
||||||
|
{
|
||||||
|
CurrentModuleHandle = Kernel32.GetModuleHandle(null);
|
||||||
|
if (CurrentModuleHandle == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
throw new Exception("Could not get module handle.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IntPtr CurrentModuleHandle { get; }
|
||||||
|
|
||||||
|
[DllImport(kernel32, CallingConvention = CallingConvention.Winapi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
|
||||||
|
public static extern IntPtr GetModuleHandle([MarshalAs(UnmanagedType.LPTStr)] string lpModuleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
109
src/Artemis.UI/SkiaSharp/User32.cs
Normal file
109
src/Artemis.UI/SkiaSharp/User32.cs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Artemis.UI.SkiaSharp
|
||||||
|
{
|
||||||
|
internal class User32
|
||||||
|
{
|
||||||
|
private const string user32 = "user32.dll";
|
||||||
|
|
||||||
|
public const uint IDC_ARROW = 32512;
|
||||||
|
|
||||||
|
public const uint IDI_APPLICATION = 32512;
|
||||||
|
public const uint IDI_WINLOGO = 32517;
|
||||||
|
|
||||||
|
public const int SW_HIDE = 0;
|
||||||
|
|
||||||
|
public const uint CS_VREDRAW = 0x1;
|
||||||
|
public const uint CS_HREDRAW = 0x2;
|
||||||
|
public const uint CS_OWNDC = 0x20;
|
||||||
|
|
||||||
|
public const uint WS_EX_CLIENTEDGE = 0x00000200;
|
||||||
|
|
||||||
|
[DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
|
||||||
|
public static extern ushort RegisterClass(ref Win32Window.WNDCLASS lpWndClass);
|
||||||
|
|
||||||
|
[DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
|
||||||
|
public static extern ushort UnregisterClass([MarshalAs(UnmanagedType.LPTStr)] string lpClassName, IntPtr hInstance);
|
||||||
|
|
||||||
|
[DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||||
|
public static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
|
||||||
|
|
||||||
|
[DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||||
|
public static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr lpIconName);
|
||||||
|
|
||||||
|
[DllImport(user32, CallingConvention = CallingConvention.Winapi)]
|
||||||
|
public static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
|
||||||
|
public static extern IntPtr CreateWindowEx(uint dwExStyle, [MarshalAs(UnmanagedType.LPTStr)] string lpClassName, [MarshalAs(UnmanagedType.LPTStr)] string lpWindowName, WindowStyles dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
|
||||||
|
|
||||||
|
public static IntPtr CreateWindow(string lpClassName, string lpWindowName, WindowStyles dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam)
|
||||||
|
{
|
||||||
|
return CreateWindowEx(0, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||||
|
public static extern IntPtr GetDC(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
|
||||||
|
|
||||||
|
[DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool DestroyWindow(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool IsWindow(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||||
|
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
||||||
|
|
||||||
|
[DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool AdjustWindowRectEx(ref RECT lpRect, WindowStyles dwStyle, bool bMenu, uint dwExStyle);
|
||||||
|
|
||||||
|
[DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct RECT
|
||||||
|
{
|
||||||
|
public int left;
|
||||||
|
public int top;
|
||||||
|
public int right;
|
||||||
|
public int bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum WindowStyles : uint
|
||||||
|
{
|
||||||
|
WS_BORDER = 0x800000,
|
||||||
|
WS_CAPTION = 0xc00000,
|
||||||
|
WS_CHILD = 0x40000000,
|
||||||
|
WS_CLIPCHILDREN = 0x2000000,
|
||||||
|
WS_CLIPSIBLINGS = 0x4000000,
|
||||||
|
WS_DISABLED = 0x8000000,
|
||||||
|
WS_DLGFRAME = 0x400000,
|
||||||
|
WS_GROUP = 0x20000,
|
||||||
|
WS_HSCROLL = 0x100000,
|
||||||
|
WS_MAXIMIZE = 0x1000000,
|
||||||
|
WS_MAXIMIZEBOX = 0x10000,
|
||||||
|
WS_MINIMIZE = 0x20000000,
|
||||||
|
WS_MINIMIZEBOX = 0x20000,
|
||||||
|
WS_OVERLAPPED = 0x0,
|
||||||
|
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_SIZEFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
|
||||||
|
WS_POPUP = 0x80000000u,
|
||||||
|
WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU,
|
||||||
|
WS_SIZEFRAME = 0x40000,
|
||||||
|
WS_SYSMENU = 0x80000,
|
||||||
|
WS_TABSTOP = 0x10000,
|
||||||
|
WS_VISIBLE = 0x10000000,
|
||||||
|
WS_VSCROLL = 0x200000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/Artemis.UI/SkiaSharp/VkContext.cs
Normal file
35
src/Artemis.UI/SkiaSharp/VkContext.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using SharpVk.Khronos;
|
||||||
|
using SkiaSharp;
|
||||||
|
using Device = SharpVk.Device;
|
||||||
|
using Instance = SharpVk.Instance;
|
||||||
|
using PhysicalDevice = SharpVk.PhysicalDevice;
|
||||||
|
using Queue = SharpVk.Queue;
|
||||||
|
|
||||||
|
namespace Artemis.UI.SkiaSharp
|
||||||
|
{
|
||||||
|
internal class VkContext : IDisposable
|
||||||
|
{
|
||||||
|
public virtual Instance Instance { get; protected set; }
|
||||||
|
|
||||||
|
public virtual PhysicalDevice PhysicalDevice { get; protected set; }
|
||||||
|
|
||||||
|
public virtual Surface Surface { get; protected set; }
|
||||||
|
|
||||||
|
public virtual Device Device { get; protected set; }
|
||||||
|
|
||||||
|
public virtual Queue GraphicsQueue { get; protected set; }
|
||||||
|
|
||||||
|
public virtual Queue PresentQueue { get; protected set; }
|
||||||
|
|
||||||
|
public virtual uint GraphicsFamily { get; protected set; }
|
||||||
|
|
||||||
|
public virtual uint PresentFamily { get; protected set; }
|
||||||
|
|
||||||
|
public virtual GRVkGetProcedureAddressDelegate GetProc { get; protected set; }
|
||||||
|
|
||||||
|
public virtual GRSharpVkGetProcedureAddressDelegate SharpVkGetProc { get; protected set; }
|
||||||
|
|
||||||
|
public virtual void Dispose() => Instance?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/Artemis.UI/SkiaSharp/Win32VkContext.cs
Normal file
83
src/Artemis.UI/SkiaSharp/Win32VkContext.cs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using SharpVk;
|
||||||
|
using SharpVk.Khronos;
|
||||||
|
|
||||||
|
namespace Artemis.UI.SkiaSharp
|
||||||
|
{
|
||||||
|
internal sealed class Win32VkContext : VkContext
|
||||||
|
{
|
||||||
|
private static readonly NativeWindow window = new NativeWindow();
|
||||||
|
|
||||||
|
public Win32VkContext()
|
||||||
|
{
|
||||||
|
Instance = Instance.Create(null, new[] { "VK_KHR_surface", "VK_KHR_win32_surface" });
|
||||||
|
|
||||||
|
PhysicalDevice = Instance.EnumeratePhysicalDevices().First();
|
||||||
|
|
||||||
|
Surface = Instance.CreateWin32Surface(Kernel32.CurrentModuleHandle, window.Handle);
|
||||||
|
|
||||||
|
(GraphicsFamily, PresentFamily) = FindQueueFamilies();
|
||||||
|
|
||||||
|
DeviceQueueCreateInfo[]? queueInfos = new[]
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
PresentQueue = Device.GetQueue(PresentFamily, 0);
|
||||||
|
|
||||||
|
GetProc = (name, instanceHandle, deviceHandle) =>
|
||||||
|
{
|
||||||
|
if (deviceHandle != IntPtr.Zero)
|
||||||
|
return Device.GetProcedureAddress(name);
|
||||||
|
|
||||||
|
return Instance.GetProcedureAddress(name);
|
||||||
|
};
|
||||||
|
|
||||||
|
SharpVkGetProc = (name, instance, device) =>
|
||||||
|
{
|
||||||
|
if (device != null)
|
||||||
|
return device.GetProcedureAddress(name);
|
||||||
|
if (instance != null)
|
||||||
|
return instance.GetProcedureAddress(name);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private (uint, uint) FindQueueFamilies()
|
||||||
|
{
|
||||||
|
QueueFamilyProperties[]? queueFamilyProperties = PhysicalDevice.GetQueueFamilyProperties();
|
||||||
|
|
||||||
|
var graphicsFamily = queueFamilyProperties
|
||||||
|
.Select((properties, index) => new { properties, index })
|
||||||
|
.SkipWhile(pair => !pair.properties.QueueFlags.HasFlag(QueueFlags.Graphics))
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (graphicsFamily == null)
|
||||||
|
throw new Exception("Unable to find graphics 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
97
src/Artemis.UI/SkiaSharp/Win32Window.cs
Normal file
97
src/Artemis.UI/SkiaSharp/Win32Window.cs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Artemis.UI.SkiaSharp
|
||||||
|
{
|
||||||
|
internal class Win32Window : IDisposable
|
||||||
|
{
|
||||||
|
private ushort classRegistration;
|
||||||
|
|
||||||
|
public string WindowClassName { get; }
|
||||||
|
|
||||||
|
public IntPtr WindowHandle { get; private set; }
|
||||||
|
|
||||||
|
public IntPtr DeviceContextHandle { get; private set; }
|
||||||
|
|
||||||
|
public Win32Window(string className)
|
||||||
|
{
|
||||||
|
WindowClassName = className;
|
||||||
|
|
||||||
|
var wc = new WNDCLASS
|
||||||
|
{
|
||||||
|
cbClsExtra = 0,
|
||||||
|
cbWndExtra = 0,
|
||||||
|
hbrBackground = IntPtr.Zero,
|
||||||
|
hCursor = User32.LoadCursor(IntPtr.Zero, (int)User32.IDC_ARROW),
|
||||||
|
hIcon = User32.LoadIcon(IntPtr.Zero, (IntPtr)User32.IDI_APPLICATION),
|
||||||
|
hInstance = Kernel32.CurrentModuleHandle,
|
||||||
|
lpfnWndProc = (WNDPROC)User32.DefWindowProc,
|
||||||
|
lpszClassName = WindowClassName,
|
||||||
|
lpszMenuName = null,
|
||||||
|
style = User32.CS_HREDRAW | User32.CS_VREDRAW | User32.CS_OWNDC
|
||||||
|
};
|
||||||
|
|
||||||
|
classRegistration = User32.RegisterClass(ref wc);
|
||||||
|
if (classRegistration == 0)
|
||||||
|
throw new Exception($"Could not register window class: {className}");
|
||||||
|
|
||||||
|
WindowHandle = User32.CreateWindow(
|
||||||
|
WindowClassName,
|
||||||
|
$"The Invisible Man ({className})",
|
||||||
|
User32.WindowStyles.WS_OVERLAPPEDWINDOW,
|
||||||
|
0, 0,
|
||||||
|
1, 1,
|
||||||
|
IntPtr.Zero, IntPtr.Zero, Kernel32.CurrentModuleHandle, IntPtr.Zero);
|
||||||
|
if (WindowHandle == IntPtr.Zero)
|
||||||
|
throw new Exception($"Could not create window: {className}");
|
||||||
|
|
||||||
|
DeviceContextHandle = User32.GetDC(WindowHandle);
|
||||||
|
if (DeviceContextHandle == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
throw new Exception($"Could not get device context: {className}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (WindowHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
if (DeviceContextHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
User32.ReleaseDC(WindowHandle, DeviceContextHandle);
|
||||||
|
DeviceContextHandle = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
User32.DestroyWindow(WindowHandle);
|
||||||
|
WindowHandle = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classRegistration != 0)
|
||||||
|
{
|
||||||
|
User32.UnregisterClass(WindowClassName, Kernel32.CurrentModuleHandle);
|
||||||
|
classRegistration = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate IntPtr WNDPROC(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct WNDCLASS
|
||||||
|
{
|
||||||
|
public uint style;
|
||||||
|
[MarshalAs(UnmanagedType.FunctionPtr)]
|
||||||
|
public WNDPROC lpfnWndProc;
|
||||||
|
public int cbClsExtra;
|
||||||
|
public int cbWndExtra;
|
||||||
|
public IntPtr hInstance;
|
||||||
|
public IntPtr hIcon;
|
||||||
|
public IntPtr hCursor;
|
||||||
|
public IntPtr hbrBackground;
|
||||||
|
[MarshalAs(UnmanagedType.LPTStr)]
|
||||||
|
public string lpszMenuName;
|
||||||
|
[MarshalAs(UnmanagedType.LPTStr)]
|
||||||
|
public string lpszClassName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -126,6 +126,16 @@
|
|||||||
"SkiaSharp.Views.Desktop.Common": "2.80.2"
|
"SkiaSharp.Views.Desktop.Common": "2.80.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"SkiaSharp.Vulkan.SharpVk": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[2.80.2, )",
|
||||||
|
"resolved": "2.80.2",
|
||||||
|
"contentHash": "qiqlbgMsSdxTsaPErtE1lXoMXolVVF9E6irmSTzlW++6BbW8tzA89n7GNsgMYJgyo2ljHZhX5ydhFn0Rkj7VHw==",
|
||||||
|
"dependencies": {
|
||||||
|
"SharpVk": "0.4.2",
|
||||||
|
"SkiaSharp": "2.80.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Stylet": {
|
"Stylet": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[1.3.5, )",
|
"requested": "[1.3.5, )",
|
||||||
@ -513,6 +523,15 @@
|
|||||||
"resolved": "1.7.1",
|
"resolved": "1.7.1",
|
||||||
"contentHash": "ljl9iVpmGOjgmxXxyulMBfl7jCLEMmTOSIrQwJJQLIm5PFhtaxRRgdQPY5ElXz+vfPKqX7Aj3RGnAN+SUN7V3w=="
|
"contentHash": "ljl9iVpmGOjgmxXxyulMBfl7jCLEMmTOSIrQwJJQLIm5PFhtaxRRgdQPY5ElXz+vfPKqX7Aj3RGnAN+SUN7V3w=="
|
||||||
},
|
},
|
||||||
|
"SharpVk": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "0.4.2",
|
||||||
|
"contentHash": "0CzZJWKw6CTmxKOXzCCyTKCD7tZB6g2+tm2VSSCXWTHlIMHxlRzbH5BaqkYCGo9Y23wp0hPuz2U3NifMH1VI6w==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime.CompilerServices.Unsafe": "4.4.0",
|
||||||
|
"System.ValueTuple": "4.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"SkiaSharp": {
|
"SkiaSharp": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "2.80.2",
|
"resolved": "2.80.2",
|
||||||
|
|||||||
@ -228,4 +228,5 @@
|
|||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=snackbar/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=snackbar/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|
||||||
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Timelines/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Timelines/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Vulkan/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
Loading…
x
Reference in New Issue
Block a user