csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
907 lines
33 KiB
907 lines
33 KiB
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using System.Runtime.InteropServices;
|
|
using Avalonia;
|
|
using Avalonia.Threading;
|
|
using Avalonia.Vulkan;
|
|
using Silk.NET.Core.Contexts;
|
|
using Silk.NET.Vulkan;
|
|
using Buffer = System.Buffer;
|
|
using Image = Silk.NET.Vulkan.Image;
|
|
// ReSharper disable RedundantUnsafeContext
|
|
// ReSharper disable RedundantEmptyObjectCreationArgumentList
|
|
// ReSharper disable MustUseReturnValue
|
|
#pragma warning disable CS0618
|
|
|
|
namespace SilkNetDemo;
|
|
|
|
public unsafe class VulkanDemoControl : VulkanControlBase
|
|
{
|
|
private float _yaw;
|
|
|
|
public static readonly DirectProperty<VulkanDemoControl, float> YawProperty =
|
|
AvaloniaProperty.RegisterDirect<VulkanDemoControl, float>("Yaw", o => o.Yaw, (o, v) => o.Yaw = v);
|
|
|
|
public float Yaw
|
|
{
|
|
get => _yaw;
|
|
set => SetAndRaise(YawProperty, ref _yaw, value);
|
|
}
|
|
|
|
private float _pitch;
|
|
|
|
public static readonly DirectProperty<VulkanDemoControl, float> PitchProperty =
|
|
AvaloniaProperty.RegisterDirect<VulkanDemoControl, float>("Pitch", o => o.Pitch, (o, v) => o.Pitch = v);
|
|
|
|
public float Pitch
|
|
{
|
|
get => _pitch;
|
|
set => SetAndRaise(PitchProperty, ref _pitch, value);
|
|
}
|
|
|
|
|
|
private float _roll;
|
|
|
|
public static readonly DirectProperty<VulkanDemoControl, float> RollProperty =
|
|
AvaloniaProperty.RegisterDirect<VulkanDemoControl, float>("Roll", o => o.Roll, (o, v) => o.Roll = v);
|
|
|
|
public float Roll
|
|
{
|
|
get => _roll;
|
|
set => SetAndRaise(RollProperty, ref _roll, value);
|
|
}
|
|
|
|
|
|
private float _disco;
|
|
|
|
public static readonly DirectProperty<VulkanDemoControl, float> DiscoProperty =
|
|
AvaloniaProperty.RegisterDirect<VulkanDemoControl, float>("Disco", o => o.Disco, (o, v) => o.Disco = v);
|
|
|
|
public float Disco
|
|
{
|
|
get => _disco;
|
|
set => SetAndRaise(DiscoProperty, ref _disco, value);
|
|
}
|
|
|
|
private string _info;
|
|
|
|
public static readonly DirectProperty<VulkanDemoControl, string> InfoProperty =
|
|
AvaloniaProperty.RegisterDirect<VulkanDemoControl, string>("Info", o => o.Info, (o, v) => o.Info = v);
|
|
|
|
public string Info
|
|
{
|
|
get => _info;
|
|
private set => SetAndRaise(InfoProperty, ref _info, value);
|
|
}
|
|
|
|
static VulkanDemoControl()
|
|
{
|
|
AffectsRender<VulkanDemoControl>(YawProperty, PitchProperty, RollProperty, DiscoProperty);
|
|
}
|
|
|
|
private ShaderModule _vertShader;
|
|
private ShaderModule _fragShader;
|
|
private PipelineLayout _pipelineLayout;
|
|
private RenderPass _renderPass;
|
|
private Pipeline _pipeline;
|
|
private Silk.NET.Vulkan.Buffer _vertexBuffer;
|
|
private DeviceMemory _vertexBufferMemory;
|
|
private Silk.NET.Vulkan.Buffer _indexBuffer;
|
|
private DeviceMemory _indexBufferMemory;
|
|
private Framebuffer[] _framebuffers;
|
|
|
|
private Image _depthImage;
|
|
private DeviceMemory _depthImageMemory;
|
|
private ImageView _depthImageView;
|
|
private VulkanCommandBufferPool _commandPool;
|
|
private Fence _fence;
|
|
private Vk _vk;
|
|
|
|
private byte[] GetShader(bool fragment)
|
|
{
|
|
var name = typeof(VulkanDemoControl).Assembly.GetManifestResourceNames()
|
|
.First(x => x.Contains((fragment ? "frag" : "vert") + ".spirv"));
|
|
using (var sr = typeof(VulkanDemoControl).Assembly.GetManifestResourceStream(name))
|
|
{
|
|
using (var mem = new MemoryStream())
|
|
{
|
|
sr.CopyTo(mem);
|
|
return mem.ToArray();
|
|
}
|
|
}
|
|
}
|
|
|
|
private ISwapchain? _previousSwapchain;
|
|
|
|
protected override void OnVulkanRender(IVulkanSharedDevice sharedDevice, ISwapchain swapchain)
|
|
{
|
|
using (sharedDevice.Device.Lock())
|
|
{
|
|
var api = _vk;
|
|
var device = new Device(sharedDevice.Device.Handle);
|
|
var physicalDevice = new PhysicalDevice(sharedDevice.Device.PhysicalDeviceHandle);
|
|
_commandPool?.FreeUsedCommandBuffers();
|
|
if (_previousSwapchain != swapchain)
|
|
CreateTemporalObjects(api, device, physicalDevice, swapchain);
|
|
|
|
_previousSwapchain = swapchain;
|
|
|
|
var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, -1, 0));
|
|
var model = Matrix4x4.CreateFromYawPitchRoll(_yaw, _pitch, _roll);
|
|
var projection =
|
|
Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)(Bounds.Width / Bounds.Height),
|
|
0.01f, 1000);
|
|
|
|
var vertexConstant = new VertextPushConstant()
|
|
{
|
|
Disco = _disco,
|
|
Model = model,
|
|
Projection = projection,
|
|
Time = (float)St.Elapsed.TotalSeconds,
|
|
View = view
|
|
};
|
|
|
|
var fragConstant = new FragmentPushConstant()
|
|
{
|
|
Disco = _disco,
|
|
Time = (float)St.Elapsed.TotalSeconds,
|
|
MinY = _minY,
|
|
MaxY = _maxY,
|
|
};
|
|
|
|
var commandBuffer = _commandPool.CreateCommandBuffer();
|
|
commandBuffer.BeginRecording();
|
|
var commandBufferHandle = new CommandBuffer(commandBuffer.Handle);
|
|
|
|
api.CmdSetViewport(commandBufferHandle, 0, 1,
|
|
new Viewport()
|
|
{
|
|
Width = (float)Bounds.Width,
|
|
Height = (float)Bounds.Height,
|
|
MaxDepth = 1,
|
|
MinDepth = 0,
|
|
X = 0,
|
|
Y = 0
|
|
});
|
|
|
|
var scissor = new Rect2D
|
|
{
|
|
Extent = new Extent2D((uint?)Bounds.Width, (uint?)Bounds.Height)
|
|
};
|
|
|
|
api.CmdSetScissor(commandBufferHandle, 0, 1, &scissor);
|
|
|
|
var clearColor = new ClearValue(new ClearColorValue(0, 0, 0, 0), new ClearDepthStencilValue(1, 0));
|
|
|
|
var clearValues = new[] { clearColor, clearColor };
|
|
|
|
|
|
fixed (ClearValue* clearValue = clearValues)
|
|
{
|
|
var beginInfo = new RenderPassBeginInfo()
|
|
{
|
|
SType = StructureType.RenderPassBeginInfo,
|
|
RenderPass = _renderPass,
|
|
Framebuffer = _framebuffers[swapchain.CurrentImageIndex],
|
|
RenderArea =
|
|
new Rect2D(new Offset2D(0, 0), new Extent2D((uint?)Bounds.Width, (uint?)Bounds.Height)),
|
|
ClearValueCount = 2,
|
|
PClearValues = clearValue
|
|
};
|
|
|
|
api.CmdBeginRenderPass(commandBufferHandle, beginInfo, SubpassContents.Inline);
|
|
}
|
|
|
|
api.CmdBindPipeline(commandBufferHandle, PipelineBindPoint.Graphics, _pipeline);
|
|
|
|
api.CmdPushConstants(commandBufferHandle, _pipelineLayout, ShaderStageFlags.ShaderStageVertexBit, 0,
|
|
(uint)Marshal.SizeOf<VertextPushConstant>(), &vertexConstant);
|
|
api.CmdPushConstants(commandBufferHandle, _pipelineLayout, ShaderStageFlags.ShaderStageFragmentBit,
|
|
(uint)Marshal.SizeOf<VertextPushConstant>(), (uint)Marshal.SizeOf<FragmentPushConstant>(),
|
|
&fragConstant);
|
|
|
|
api.CmdBindVertexBuffers(commandBufferHandle, 0, 1, _vertexBuffer, 0);
|
|
api.CmdBindIndexBuffer(commandBufferHandle, _indexBuffer, 0, IndexType.Uint16);
|
|
|
|
api.CmdDrawIndexed(commandBufferHandle, (uint)_indices.Length, 1, 0, 0, 0);
|
|
|
|
api.CmdEndRenderPass(commandBufferHandle);
|
|
|
|
commandBuffer.Submit();
|
|
|
|
if (_disco > 0.01)
|
|
Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
|
|
}
|
|
}
|
|
|
|
protected override void OnVulkanDeInit(IVulkanSharedDevice sharedDevice)
|
|
{
|
|
|
|
if (_isInit)
|
|
{
|
|
lock (sharedDevice.Device.Lock())
|
|
{
|
|
var api = _vk;
|
|
var device = new Device(sharedDevice.Device.Handle);
|
|
_vk.DeviceWaitIdle(device);
|
|
|
|
DestroyTemporalObjects(api, device);
|
|
|
|
api.DestroyShaderModule(device, _vertShader, null);
|
|
api.DestroyShaderModule(device, _fragShader, null);
|
|
|
|
api.DestroyBuffer(device, _vertexBuffer, null);
|
|
api.FreeMemory(device, _vertexBufferMemory, null);
|
|
|
|
api.DestroyBuffer(device, _indexBuffer, null);
|
|
api.FreeMemory(device, _indexBufferMemory, null);
|
|
api.DestroyFence(device, _fence, null);
|
|
|
|
_commandPool.Dispose();
|
|
}
|
|
}
|
|
|
|
_isInit = false;
|
|
}
|
|
|
|
public unsafe void DestroyTemporalObjects(Vk api, Device device)
|
|
{
|
|
if (_isInit)
|
|
{
|
|
if (_renderPass.Handle != 0)
|
|
{
|
|
api.DestroyImageView(device, _depthImageView, null);
|
|
api.DestroyImage(device, _depthImage, null);
|
|
api.FreeMemory(device, _depthImageMemory, null);
|
|
|
|
foreach (var fb in _framebuffers)
|
|
api.DestroyFramebuffer(device, fb, null);
|
|
api.DestroyPipeline(device, _pipeline, null);
|
|
api.DestroyPipelineLayout(device, _pipelineLayout, null);
|
|
api.DestroyRenderPass(device, _renderPass, null);
|
|
|
|
_depthImage = default;
|
|
_depthImageView = default;
|
|
_depthImageView = default;
|
|
_framebuffers = Array.Empty<Framebuffer>();
|
|
_pipeline = default;
|
|
_renderPass = default;
|
|
_pipelineLayout = default;
|
|
}
|
|
}
|
|
}
|
|
|
|
private unsafe void CreateDepthAttachment(Vk api, Device device, PhysicalDevice physicalDevice)
|
|
{
|
|
var imageCreateInfo = new ImageCreateInfo
|
|
{
|
|
SType = StructureType.ImageCreateInfo,
|
|
ImageType = ImageType.ImageType2D,
|
|
Format = Format.D32Sfloat,
|
|
Extent =
|
|
new Extent3D((uint?)Bounds.Width,
|
|
(uint?)Bounds.Height, 1),
|
|
MipLevels = 1,
|
|
ArrayLayers = 1,
|
|
Samples = SampleCountFlags.SampleCount1Bit,
|
|
Tiling = ImageTiling.Optimal,
|
|
Usage = ImageUsageFlags.ImageUsageDepthStencilAttachmentBit,
|
|
SharingMode = SharingMode.Exclusive,
|
|
InitialLayout = ImageLayout.Undefined,
|
|
Flags = ImageCreateFlags.ImageCreateMutableFormatBit
|
|
};
|
|
|
|
api
|
|
.CreateImage(device, imageCreateInfo, null, out _depthImage).ThrowOnError();
|
|
|
|
api.GetImageMemoryRequirements(device, _depthImage,
|
|
out var memoryRequirements);
|
|
|
|
var memoryAllocateInfo = new MemoryAllocateInfo
|
|
{
|
|
SType = StructureType.MemoryAllocateInfo,
|
|
AllocationSize = memoryRequirements.Size,
|
|
MemoryTypeIndex = (uint)FindSuitableMemoryTypeIndex(api,
|
|
physicalDevice,
|
|
memoryRequirements.MemoryTypeBits, MemoryPropertyFlags.MemoryPropertyDeviceLocalBit)
|
|
};
|
|
|
|
api.AllocateMemory(device, memoryAllocateInfo, null,
|
|
out _depthImageMemory).ThrowOnError();
|
|
|
|
api.BindImageMemory(device, _depthImage, _depthImageMemory, 0);
|
|
|
|
var componentMapping = new ComponentMapping(
|
|
ComponentSwizzle.R,
|
|
ComponentSwizzle.G,
|
|
ComponentSwizzle.B,
|
|
ComponentSwizzle.A);
|
|
|
|
var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectDepthBit,
|
|
0, 1, 0, 1);
|
|
|
|
var imageViewCreateInfo = new ImageViewCreateInfo
|
|
{
|
|
SType = StructureType.ImageViewCreateInfo,
|
|
Image = _depthImage,
|
|
ViewType = ImageViewType.ImageViewType2D,
|
|
Format = Format.D32Sfloat,
|
|
Components = componentMapping,
|
|
SubresourceRange = subresourceRange
|
|
};
|
|
|
|
api
|
|
.CreateImageView(device, imageViewCreateInfo, null, out _depthImageView)
|
|
.ThrowOnError();
|
|
}
|
|
|
|
private unsafe void CreateTemporalObjects(Vk api, Device device, PhysicalDevice physicalDevice,
|
|
ISwapchain swapchain)
|
|
{
|
|
DestroyTemporalObjects(api, device);
|
|
|
|
CreateDepthAttachment(api, device, physicalDevice);
|
|
|
|
var current = swapchain.GetImage(swapchain.CurrentImageIndex);
|
|
// create renderpasses
|
|
var colorAttachment = new AttachmentDescription()
|
|
{
|
|
Format = (Format)current.Format,
|
|
Samples = SampleCountFlags.SampleCount1Bit,
|
|
LoadOp = AttachmentLoadOp.Clear,
|
|
StoreOp = AttachmentStoreOp.Store,
|
|
InitialLayout = (ImageLayout)current.Layout,
|
|
FinalLayout = (ImageLayout)current.Layout,
|
|
StencilLoadOp = AttachmentLoadOp.DontCare,
|
|
StencilStoreOp = AttachmentStoreOp.DontCare
|
|
};
|
|
|
|
var depthAttachment = new AttachmentDescription()
|
|
{
|
|
Format = Format.D32Sfloat,
|
|
Samples = SampleCountFlags.SampleCount1Bit,
|
|
LoadOp = AttachmentLoadOp.Clear,
|
|
StoreOp = AttachmentStoreOp.DontCare,
|
|
InitialLayout = ImageLayout.Undefined,
|
|
FinalLayout = ImageLayout.DepthStencilAttachmentOptimal,
|
|
StencilLoadOp = AttachmentLoadOp.DontCare,
|
|
StencilStoreOp = AttachmentStoreOp.DontCare
|
|
};
|
|
|
|
var subpassDependency = new SubpassDependency()
|
|
{
|
|
SrcSubpass = Vk.SubpassExternal,
|
|
DstSubpass = 0,
|
|
SrcStageMask = PipelineStageFlags.PipelineStageColorAttachmentOutputBit,
|
|
SrcAccessMask = 0,
|
|
DstStageMask = PipelineStageFlags.PipelineStageColorAttachmentOutputBit,
|
|
DstAccessMask = AccessFlags.AccessColorAttachmentWriteBit
|
|
};
|
|
|
|
var colorAttachmentReference = new AttachmentReference()
|
|
{
|
|
Attachment = 0, Layout = ImageLayout.ColorAttachmentOptimal
|
|
};
|
|
|
|
var depthAttachmentReference = new AttachmentReference()
|
|
{
|
|
Attachment = 1, Layout = ImageLayout.DepthStencilAttachmentOptimal
|
|
};
|
|
|
|
var subpassDescription = new SubpassDescription()
|
|
{
|
|
PipelineBindPoint = PipelineBindPoint.Graphics,
|
|
ColorAttachmentCount = 1,
|
|
PColorAttachments = &colorAttachmentReference,
|
|
PDepthStencilAttachment = &depthAttachmentReference
|
|
};
|
|
|
|
var attachments = new[] { colorAttachment, depthAttachment };
|
|
|
|
fixed (AttachmentDescription* atPtr = attachments)
|
|
{
|
|
var renderPassCreateInfo = new RenderPassCreateInfo()
|
|
{
|
|
SType = StructureType.RenderPassCreateInfo,
|
|
AttachmentCount = (uint)attachments.Length,
|
|
PAttachments = atPtr,
|
|
SubpassCount = 1,
|
|
PSubpasses = &subpassDescription,
|
|
DependencyCount = 1,
|
|
PDependencies = &subpassDependency
|
|
};
|
|
|
|
api.CreateRenderPass(device, renderPassCreateInfo, null, out _renderPass).ThrowOnError();
|
|
|
|
|
|
_framebuffers = new Framebuffer[swapchain.ImageCount];
|
|
for (var c = 0; c < _framebuffers.Length; c++)
|
|
{
|
|
// create framebuffer
|
|
var frameBufferAttachments = new[] { new ImageView((ulong)swapchain.GetImage(c).ViewHandle), _depthImageView };
|
|
|
|
fixed (ImageView* frAtPtr = frameBufferAttachments)
|
|
{
|
|
var framebufferCreateInfo = new FramebufferCreateInfo()
|
|
{
|
|
SType = StructureType.FramebufferCreateInfo,
|
|
RenderPass = _renderPass,
|
|
AttachmentCount = (uint)frameBufferAttachments.Length,
|
|
PAttachments = frAtPtr,
|
|
Width = (uint)current.PixelSize.Width,
|
|
Height = (uint)current.PixelSize.Height,
|
|
Layers = 1
|
|
};
|
|
|
|
api.CreateFramebuffer(device, framebufferCreateInfo, null, out var fb).ThrowOnError();
|
|
_framebuffers[c] = fb;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create pipeline
|
|
var pname = Marshal.StringToHGlobalAnsi("main");
|
|
var vertShaderStageInfo = new PipelineShaderStageCreateInfo()
|
|
{
|
|
SType = StructureType.PipelineShaderStageCreateInfo,
|
|
Stage = ShaderStageFlags.ShaderStageVertexBit,
|
|
Module = _vertShader,
|
|
PName = (byte*)pname,
|
|
};
|
|
var fragShaderStageInfo = new PipelineShaderStageCreateInfo()
|
|
{
|
|
SType = StructureType.PipelineShaderStageCreateInfo,
|
|
Stage = ShaderStageFlags.ShaderStageFragmentBit,
|
|
Module = _fragShader,
|
|
PName = (byte*)pname,
|
|
};
|
|
|
|
var stages = new[] { vertShaderStageInfo, fragShaderStageInfo };
|
|
|
|
var bindingDescription = Vertex.VertexInputBindingDescription;
|
|
var attributeDescription = Vertex.VertexInputAttributeDescription;
|
|
|
|
fixed (VertexInputAttributeDescription* attrPtr = attributeDescription)
|
|
{
|
|
var vertextInputInfo = new PipelineVertexInputStateCreateInfo()
|
|
{
|
|
SType = StructureType.PipelineVertexInputStateCreateInfo,
|
|
VertexAttributeDescriptionCount = (uint)attributeDescription.Length,
|
|
VertexBindingDescriptionCount = 1,
|
|
PVertexAttributeDescriptions = attrPtr,
|
|
PVertexBindingDescriptions = &bindingDescription
|
|
};
|
|
|
|
var inputAssembly = new PipelineInputAssemblyStateCreateInfo()
|
|
{
|
|
SType = StructureType.PipelineInputAssemblyStateCreateInfo,
|
|
Topology = PrimitiveTopology.TriangleList,
|
|
PrimitiveRestartEnable = false
|
|
};
|
|
|
|
var viewport = new Viewport()
|
|
{
|
|
X = 0,
|
|
Y = 0,
|
|
Width = (float)Bounds.Width,
|
|
Height = (float)Bounds.Height,
|
|
MinDepth = 0,
|
|
MaxDepth = 1
|
|
};
|
|
|
|
var scissor = new Rect2D()
|
|
{
|
|
Offset = new Offset2D(0, 0), Extent = new Extent2D((uint)viewport.Width, (uint)viewport.Height)
|
|
};
|
|
|
|
var pipelineViewPortCreateInfo = new PipelineViewportStateCreateInfo()
|
|
{
|
|
SType = StructureType.PipelineViewportStateCreateInfo,
|
|
ViewportCount = 1,
|
|
PViewports = &viewport,
|
|
ScissorCount = 1,
|
|
PScissors = &scissor
|
|
};
|
|
|
|
var rasterizerStateCreateInfo = new PipelineRasterizationStateCreateInfo()
|
|
{
|
|
SType = StructureType.PipelineRasterizationStateCreateInfo,
|
|
DepthClampEnable = false,
|
|
RasterizerDiscardEnable = false,
|
|
PolygonMode = PolygonMode.Fill,
|
|
LineWidth = 1,
|
|
CullMode = CullModeFlags.CullModeNone,
|
|
DepthBiasEnable = false
|
|
};
|
|
|
|
var multisampleStateCreateInfo = new PipelineMultisampleStateCreateInfo()
|
|
{
|
|
SType = StructureType.PipelineMultisampleStateCreateInfo,
|
|
SampleShadingEnable = false,
|
|
RasterizationSamples = SampleCountFlags.SampleCount1Bit
|
|
};
|
|
|
|
var depthStencilCreateInfo = new PipelineDepthStencilStateCreateInfo()
|
|
{
|
|
SType = StructureType.PipelineDepthStencilStateCreateInfo,
|
|
StencilTestEnable = false,
|
|
DepthCompareOp = CompareOp.Less,
|
|
DepthTestEnable = true,
|
|
DepthWriteEnable = true,
|
|
DepthBoundsTestEnable = false,
|
|
};
|
|
|
|
var colorBlendAttachmentState = new PipelineColorBlendAttachmentState()
|
|
{
|
|
ColorWriteMask = ColorComponentFlags.ColorComponentABit |
|
|
ColorComponentFlags.ColorComponentRBit |
|
|
ColorComponentFlags.ColorComponentGBit |
|
|
ColorComponentFlags.ColorComponentBBit,
|
|
BlendEnable = false
|
|
};
|
|
|
|
var colorBlendState = new PipelineColorBlendStateCreateInfo()
|
|
{
|
|
SType = StructureType.PipelineColorBlendStateCreateInfo,
|
|
LogicOpEnable = false,
|
|
AttachmentCount = 1,
|
|
PAttachments = &colorBlendAttachmentState
|
|
};
|
|
|
|
var dynamicStates = new[] { DynamicState.Viewport, DynamicState.Scissor };
|
|
|
|
fixed (DynamicState* states = dynamicStates)
|
|
{
|
|
var dynamicStateCreateInfo = new PipelineDynamicStateCreateInfo()
|
|
{
|
|
SType = StructureType.PipelineDynamicStateCreateInfo,
|
|
DynamicStateCount = (uint)dynamicStates.Length,
|
|
PDynamicStates = states
|
|
};
|
|
|
|
var vertexPushConstantRange = new PushConstantRange()
|
|
{
|
|
Offset = 0,
|
|
Size = (uint)Marshal.SizeOf<VertextPushConstant>(),
|
|
StageFlags = ShaderStageFlags.ShaderStageVertexBit
|
|
};
|
|
|
|
var fragPushConstantRange = new PushConstantRange()
|
|
{
|
|
Offset = vertexPushConstantRange.Size,
|
|
Size = (uint)Marshal.SizeOf<FragmentPushConstant>(),
|
|
StageFlags = ShaderStageFlags.ShaderStageFragmentBit
|
|
};
|
|
|
|
var constants = new[] { vertexPushConstantRange, fragPushConstantRange };
|
|
|
|
fixed (PushConstantRange* constant = constants)
|
|
{
|
|
var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo()
|
|
{
|
|
SType = StructureType.PipelineLayoutCreateInfo,
|
|
PushConstantRangeCount = (uint)constants.Length,
|
|
PPushConstantRanges = constant,
|
|
SetLayoutCount = 0
|
|
};
|
|
|
|
api.CreatePipelineLayout(device, pipelineLayoutCreateInfo, null, out _pipelineLayout)
|
|
.ThrowOnError();
|
|
}
|
|
|
|
|
|
fixed (PipelineShaderStageCreateInfo* stPtr = stages)
|
|
{
|
|
var pipelineCreateInfo = new GraphicsPipelineCreateInfo()
|
|
{
|
|
SType = StructureType.GraphicsPipelineCreateInfo,
|
|
StageCount = 2,
|
|
PStages = stPtr,
|
|
PVertexInputState = &vertextInputInfo,
|
|
PInputAssemblyState = &inputAssembly,
|
|
PViewportState = &pipelineViewPortCreateInfo,
|
|
PRasterizationState = &rasterizerStateCreateInfo,
|
|
PMultisampleState = &multisampleStateCreateInfo,
|
|
PDepthStencilState = &depthStencilCreateInfo,
|
|
PColorBlendState = &colorBlendState,
|
|
PDynamicState = &dynamicStateCreateInfo,
|
|
Layout = _pipelineLayout,
|
|
RenderPass = _renderPass,
|
|
Subpass = 0,
|
|
BasePipelineHandle = _pipeline.Handle != 0 ? _pipeline : new Pipeline(),
|
|
BasePipelineIndex = _pipeline.Handle != 0 ? 0 : -1
|
|
};
|
|
|
|
api.CreateGraphicsPipelines(device, new PipelineCache(), 1, &pipelineCreateInfo, null,
|
|
out _pipeline).ThrowOnError();
|
|
}
|
|
}
|
|
}
|
|
|
|
Marshal.FreeHGlobal(pname);
|
|
_isInit = true;
|
|
}
|
|
|
|
private unsafe void CreateBuffers(Vk api, Device device, IVulkanSharedDevice sharedDevice)
|
|
{
|
|
// vertex buffer
|
|
var vertexSize = Marshal.SizeOf<Vertex>();
|
|
|
|
var bufferInfo = new BufferCreateInfo()
|
|
{
|
|
SType = StructureType.BufferCreateInfo,
|
|
Size = (ulong)(_points.Length * vertexSize),
|
|
Usage = BufferUsageFlags.BufferUsageVertexBufferBit,
|
|
SharingMode = SharingMode.Exclusive
|
|
};
|
|
|
|
api.CreateBuffer(device, bufferInfo, null, out _vertexBuffer).ThrowOnError();
|
|
|
|
api.GetBufferMemoryRequirements(device, _vertexBuffer, out var memoryRequirements);
|
|
|
|
var physicalDevice = new PhysicalDevice(sharedDevice.Device.PhysicalDeviceHandle);
|
|
|
|
var memoryAllocateInfo = new MemoryAllocateInfo
|
|
{
|
|
SType = StructureType.MemoryAllocateInfo,
|
|
AllocationSize = memoryRequirements.Size,
|
|
MemoryTypeIndex = (uint)FindSuitableMemoryTypeIndex(api,
|
|
physicalDevice,
|
|
memoryRequirements.MemoryTypeBits,
|
|
MemoryPropertyFlags.MemoryPropertyHostCoherentBit |
|
|
MemoryPropertyFlags.MemoryPropertyHostVisibleBit)
|
|
};
|
|
|
|
api.AllocateMemory(device, memoryAllocateInfo, null, out _vertexBufferMemory).ThrowOnError();
|
|
api.BindBufferMemory(device, _vertexBuffer, _vertexBufferMemory, 0);
|
|
|
|
fixed (Vertex* points = _points)
|
|
{
|
|
void* pointer = null;
|
|
api.MapMemory(device, _vertexBufferMemory, 0, bufferInfo.Size, 0, ref pointer);
|
|
|
|
Buffer.MemoryCopy(points, pointer, bufferInfo.Size, bufferInfo.Size);
|
|
api.UnmapMemory(device, _vertexBufferMemory);
|
|
}
|
|
|
|
var indexSize = Marshal.SizeOf<ushort>();
|
|
|
|
bufferInfo = new BufferCreateInfo()
|
|
{
|
|
SType = StructureType.BufferCreateInfo,
|
|
Size = (ulong)(_indices.Length * indexSize),
|
|
Usage = BufferUsageFlags.BufferUsageIndexBufferBit,
|
|
SharingMode = SharingMode.Exclusive
|
|
};
|
|
|
|
api.CreateBuffer(device, bufferInfo, null, out _indexBuffer).ThrowOnError();
|
|
|
|
api.GetBufferMemoryRequirements(device, _indexBuffer, out memoryRequirements);
|
|
|
|
memoryAllocateInfo = new MemoryAllocateInfo
|
|
{
|
|
SType = StructureType.MemoryAllocateInfo,
|
|
AllocationSize = memoryRequirements.Size,
|
|
MemoryTypeIndex = (uint)FindSuitableMemoryTypeIndex(api,
|
|
physicalDevice,
|
|
memoryRequirements.MemoryTypeBits,
|
|
MemoryPropertyFlags.MemoryPropertyHostCoherentBit |
|
|
MemoryPropertyFlags.MemoryPropertyHostVisibleBit)
|
|
};
|
|
|
|
api.AllocateMemory(device, memoryAllocateInfo, null, out _indexBufferMemory).ThrowOnError();
|
|
api.BindBufferMemory(device, _indexBuffer, _indexBufferMemory, 0);
|
|
|
|
fixed (ushort* indice = _indices)
|
|
{
|
|
void* pointer = null;
|
|
api.MapMemory(device, _indexBufferMemory, 0, bufferInfo.Size, 0, ref pointer);
|
|
|
|
Buffer.MemoryCopy(indice, pointer, bufferInfo.Size, bufferInfo.Size);
|
|
api.UnmapMemory(device, _indexBufferMemory);
|
|
}
|
|
}
|
|
|
|
private static int FindSuitableMemoryTypeIndex(Vk api, PhysicalDevice physicalDevice, uint memoryTypeBits,
|
|
MemoryPropertyFlags flags)
|
|
{
|
|
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties);
|
|
|
|
for (var i = 0; i < properties.MemoryTypeCount; i++)
|
|
{
|
|
var type = properties.MemoryTypes[i];
|
|
|
|
if ((memoryTypeBits & (1 << i)) != 0 && type.PropertyFlags.HasFlag(flags)) return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static Vk GetApi(IVulkanDevice device) =>
|
|
new(new LamdaNativeContext(name =>
|
|
{
|
|
var deviceApi = device.Instance.GetDeviceProcAddress(device.Handle, name);
|
|
if (deviceApi != IntPtr.Zero)
|
|
return deviceApi;
|
|
var instanceApi = device.Instance.GetInstanceProcAddress(device.Instance.Handle, name);
|
|
if (instanceApi != IntPtr.Zero)
|
|
return instanceApi;
|
|
return device.Instance.GetInstanceProcAddress(IntPtr.Zero, name);
|
|
}));
|
|
|
|
|
|
protected override void OnVulkanInit(IVulkanSharedDevice sharedDevice)
|
|
{
|
|
using (sharedDevice.Device.Lock())
|
|
{
|
|
_vk = GetApi(sharedDevice.Device);
|
|
|
|
var api = _vk;
|
|
var device = new Device(sharedDevice.Device.Handle);
|
|
api.DeviceWaitIdle(device);
|
|
|
|
|
|
/*var deviceName = platformInterface.PhysicalDevice.DeviceName;
|
|
var version = platformInterface.PhysicalDevice.ApiVersion;
|
|
|
|
Info = $"Renderer: {deviceName} Version: {version.Major}.{version.Minor}.{version.Revision}";
|
|
*/
|
|
var vertShaderData = GetShader(false);
|
|
var fragShaderData = GetShader(true);
|
|
|
|
fixed (byte* ptr = vertShaderData)
|
|
{
|
|
var shaderCreateInfo = new ShaderModuleCreateInfo()
|
|
{
|
|
SType = StructureType.ShaderModuleCreateInfo,
|
|
CodeSize = (nuint)vertShaderData.Length,
|
|
PCode = (uint*)ptr,
|
|
};
|
|
|
|
api.CreateShaderModule(device, shaderCreateInfo, null, out _vertShader);
|
|
}
|
|
|
|
fixed (byte* ptr = fragShaderData)
|
|
{
|
|
var shaderCreateInfo = new ShaderModuleCreateInfo()
|
|
{
|
|
SType = StructureType.ShaderModuleCreateInfo,
|
|
CodeSize = (nuint)fragShaderData.Length,
|
|
PCode = (uint*)ptr,
|
|
};
|
|
|
|
api.CreateShaderModule(device, shaderCreateInfo, null, out _fragShader);
|
|
}
|
|
|
|
CreateBuffers(api, device, sharedDevice);
|
|
|
|
var fenceCreateInfo = new FenceCreateInfo()
|
|
{
|
|
SType = StructureType.FenceCreateInfo,
|
|
Flags = FenceCreateFlags.FenceCreateSignaledBit
|
|
};
|
|
|
|
api.CreateFence(device, fenceCreateInfo, null, out _fence);
|
|
_commandPool = new VulkanCommandBufferPool(api, device, new Queue(sharedDevice.Device.MainQueueHandle),
|
|
sharedDevice.Device.GraphicsQueueFamilyIndex);
|
|
}
|
|
}
|
|
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
private struct Vertex
|
|
{
|
|
public Vector3 Position;
|
|
public Vector3 Normal;
|
|
|
|
public static unsafe VertexInputBindingDescription VertexInputBindingDescription =>
|
|
new VertexInputBindingDescription()
|
|
{
|
|
Binding = 0,
|
|
Stride = (uint)Marshal.SizeOf<Vertex>(),
|
|
InputRate = VertexInputRate.Vertex
|
|
};
|
|
|
|
public static unsafe VertexInputAttributeDescription[] VertexInputAttributeDescription
|
|
{
|
|
get
|
|
{
|
|
return new[]
|
|
{
|
|
new VertexInputAttributeDescription
|
|
{
|
|
Binding = 0,
|
|
Location = 0,
|
|
Format = Format.R32G32B32Sfloat,
|
|
Offset = (uint)Marshal.OffsetOf<Vertex>("Position")
|
|
},
|
|
new VertexInputAttributeDescription
|
|
{
|
|
Binding = 0,
|
|
Location = 1,
|
|
Format = Format.R32G32B32Sfloat,
|
|
Offset = (uint)Marshal.OffsetOf<Vertex>("Normal")
|
|
}
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
private readonly Vertex[] _points;
|
|
private readonly ushort[] _indices;
|
|
private readonly float _minY;
|
|
private readonly float _maxY;
|
|
|
|
|
|
static Stopwatch St = Stopwatch.StartNew();
|
|
private bool _isInit;
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
private struct VertextPushConstant
|
|
{
|
|
public Matrix4x4 Model;
|
|
public Matrix4x4 Projection;
|
|
public Matrix4x4 View;
|
|
|
|
public float Time;
|
|
public float Disco;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
private struct FragmentPushConstant
|
|
{
|
|
public float MaxY;
|
|
public float MinY;
|
|
|
|
public float Time;
|
|
public float Disco;
|
|
}
|
|
|
|
public VulkanDemoControl()
|
|
{
|
|
var name = GetType().Assembly.GetManifestResourceNames().First(x => x.Contains("teapot.bin"));
|
|
using (var sr = new BinaryReader(GetType().Assembly.GetManifestResourceStream(name)!))
|
|
{
|
|
var buf = new byte[sr.ReadInt32()];
|
|
sr.Read(buf, 0, buf.Length);
|
|
var points = new float[buf.Length / 4];
|
|
Buffer.BlockCopy(buf, 0, points, 0, buf.Length);
|
|
buf = new byte[sr.ReadInt32()];
|
|
sr.Read(buf, 0, buf.Length);
|
|
_indices = new ushort[buf.Length / 2];
|
|
Buffer.BlockCopy(buf, 0, _indices, 0, buf.Length);
|
|
_points = new Vertex[points.Length / 3];
|
|
for (var primitive = 0; primitive < points.Length / 3; primitive++)
|
|
{
|
|
var srci = primitive * 3;
|
|
_points[primitive] = new Vertex
|
|
{
|
|
Position = new Vector3(points[srci], points[srci + 1], points[srci + 2])
|
|
};
|
|
}
|
|
|
|
for (int i = 0; i < _indices.Length; i += 3)
|
|
{
|
|
Vector3 a = _points[_indices[i]].Position;
|
|
Vector3 b = _points[_indices[i + 1]].Position;
|
|
Vector3 c = _points[_indices[i + 2]].Position;
|
|
var normal = Vector3.Normalize(Vector3.Cross(c - b, a - b));
|
|
|
|
_points[_indices[i]].Normal += normal;
|
|
_points[_indices[i + 1]].Normal += normal;
|
|
_points[_indices[i + 2]].Normal += normal;
|
|
}
|
|
|
|
for (int i = 0; i < _points.Length; i++)
|
|
{
|
|
_points[i].Normal = Vector3.Normalize(_points[i].Normal);
|
|
_maxY = Math.Max(_maxY, _points[i].Position.Y);
|
|
_minY = Math.Min(_minY, _points[i].Position.Y);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|