From: Jean-Philippe Bruyère Date: Thu, 9 Dec 2021 15:39:38 +0000 (+0100) Subject: rename utils.cs to helpers.cs X-Git-Tag: v0.2.5-beta~2 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=aeb75f9f55a750b2f95cafafee96abfa234432c2;p=jp%2Fvke.net.git rename utils.cs to helpers.cs --- diff --git a/vke/src/Helpers.cs b/vke/src/Helpers.cs new file mode 100644 index 0000000..86d026b --- /dev/null +++ b/vke/src/Helpers.cs @@ -0,0 +1,482 @@ +// Copyright (c) 2019-2022 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Xml.Serialization; +using Vulkan; + +namespace vke { + public static partial class Helpers { + /// Throw an erro if VkResult != Success. + static void xmlMakeTypeFieldsAsAttributes (Type t, ref XmlAttributeOverrides overrides) + { + foreach (FieldInfo fi in t.GetFields (BindingFlags.Public | BindingFlags.Instance)) + overrides.Add (t, fi.Name, new XmlAttributes { XmlAttribute = new XmlAttributeAttribute () }); + } + public static XmlAttributeOverrides GetXmlOverrides () + { + XmlAttributeOverrides xmlAttributeOverrides = new XmlAttributeOverrides (); + //Assembly avk = Assembly.GetAssembly (typeof (VkInstance)); + xmlMakeTypeFieldsAsAttributes (typeof (VkDescriptorPoolSize), ref xmlAttributeOverrides); + return xmlAttributeOverrides; + } + static bool tryFindResource (Assembly a, string resId, out Stream stream) { + stream = null; + if (a == null) + return false; + stream = a.GetManifestResourceStream (resId); + return stream != null; + } + /// + /// Return a file or embedded resource stream. + /// + /// + /// Embedded resource path start with `#`. The entry assembly is always searched first to be able + /// to override resource defined in satellite assemblies. + /// To enforce assembly name selection, use `:` to split assembly and resource (ex; "Assembly:LogicalName"), + /// else assembly names will be determined with the first, or the two firsts names in the path string. + /// (ex: AssemblyName.filename.ext then Assembly.Name.filename.ext) + /// + /// The stream from path. + /// The file or stream path. Embedded resource path starts with '#' or contains ':'. + public static Stream GetStreamFromPath (string path) { + if (path.StartsWith("#", StringComparison.Ordinal)) + { + Stream stream = null; + string[] assemblyNames = null; + Assembly assembly = null; + + string resId = path.Substring(1); + + if (tryFindResource(Assembly.GetEntryAssembly(), resId, out stream)) + return stream; + + if (path.Contains(":", StringComparison.Ordinal)) { + assemblyNames = resId.Split (':'); + assembly = AppDomain.CurrentDomain.GetAssemblies ().FirstOrDefault (aa => aa.GetName ().Name == assemblyNames[0]); + if (assembly == null) + throw new Exception("Assembly not found: " + path); + if (tryFindResource(assembly, assemblyNames[1], out stream)) + return stream; + throw new Exception("Embedded resource not found in assembly: " + path); + } + + assemblyNames = resId.Split('.'); + assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(aa => aa.GetName().Name == assemblyNames[0]); + if (assembly == null && assemblyNames.Length > 3) + assembly = AppDomain.CurrentDomain.GetAssemblies() + .FirstOrDefault(aa => aa.GetName().Name == $"{assemblyNames[0]}.{assemblyNames[1]}"); + if (assembly != null && tryFindResource(assembly, resId, out stream)) + return stream; + throw new Exception("Resource not found: " + path); + } + if (!File.Exists (path)) + throw new FileNotFoundException ("File not found: ", path); + return new FileStream (path, FileMode.Open, FileAccess.Read); + } + /// Convert angle from degree to radian. + public static float DegreesToRadians (float degrees) { + return degrees * (float)Math.PI / 180f; + } + + /// + /// Populate a Vector3 with values from a float array + /// + public static void FromFloatArray (ref Vector3 v, float[] floats) { + v = Unsafe.As(ref floats)[0]; + } + /// + /// Populate a Vector4 with values from a float array + /// + public static void FromFloatArray (ref Vector4 v, float[] floats) { + MemoryMarshal.Cast(floats.AsSpan()); + } + /// + /// Populate a Quaternion with values from a float array + /// + public static void FromFloatArray (ref Quaternion q, float[] floats) { + q = Unsafe.As(ref floats)[0]; + } + /// + /// Populate a Vector2 with values from a byte array starting at offset + /// + public static void FromByteArray (ref Vector2 v, byte[] byteArray, int offset) { + v = Unsafe.As(ref Unsafe.AsRef(byteArray.SubArray(offset, 8)))[0]; + } + /// + /// Populate a Vector3 with values from a byte array starting at offset + /// + public static void FromByteArray (ref Vector3 v, byte[] byteArray, int offset) { + v = Unsafe.As(ref Unsafe.AsRef(byteArray.SubArray(offset, 12)))[0]; + } + /// + /// Populate a Vector4 with values from a byte array starting at offset + /// + public static void FromByteArray (ref Vector4 v, byte[] byteArray, int offset) { + v = Unsafe.As(ref Unsafe.AsRef(byteArray.SubArray(offset, 16)))[0]; + } + /// + /// Populate a Quaternion with values from a byte array starting at offset + /// + public static void FromByteArray (ref Quaternion v, byte[] byteArray, int offset) { + v = Unsafe.As(ref Unsafe.AsRef(byteArray.SubArray(offset, 16)))[0]; + } + + #region Extensions methods + public static void ImportFloatArray (this ref Vector3 v, float[] floats) { + v = Unsafe.As(ref floats)[0]; + } + public static Vector3 Transform (this Vector3 v, ref Matrix4x4 mat, bool translate = false) { + Vector4 v4 = Vector4.Transform (new Vector4 (v, translate ? 1f : 0f), mat); + return new Vector3 (v4.X, v4.Y, v4.Z); + } + public static Vector3 ToVector3 (this Vector4 v) { + return Unsafe.As(ref v); + } + public static T[] SubArray(this T[] array, int offset, int length) + { + T[] result = new T[length]; + Array.Copy(array, offset, result, 0, length); + return result; + } + #endregion + + // Fixed sub resource on first mip level and layer + public static void setImageLayout ( + VkCommandBuffer cmdbuffer, + VkImage image, + VkImageAspectFlags aspectMask, + VkImageLayout oldImageLayout, + VkImageLayout newImageLayout, + VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands, + VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands) { + VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange { + aspectMask = aspectMask, + baseMipLevel = 0, + levelCount = 1, + layerCount = 1, + }; + setImageLayout (cmdbuffer, image, aspectMask, oldImageLayout, newImageLayout, subresourceRange); + } + + // Create an image memory barrier for changing the layout of + // an image and put it into an active command buffer + // See chapter 11.4 "Image Layout" for details + + public static void setImageLayout ( + VkCommandBuffer cmdbuffer, + VkImage image, + VkImageAspectFlags aspectMask, + VkImageLayout oldImageLayout, + VkImageLayout newImageLayout, + VkImageSubresourceRange subresourceRange, + VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands, + VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands) { + // Create an image barrier object + VkImageMemoryBarrier imageMemoryBarrier = default; + imageMemoryBarrier.srcQueueFamilyIndex = Vk.QueueFamilyIgnored; + imageMemoryBarrier.dstQueueFamilyIndex = Vk.QueueFamilyIgnored; + imageMemoryBarrier.oldLayout = oldImageLayout; + imageMemoryBarrier.newLayout = newImageLayout; + imageMemoryBarrier.image = image; + imageMemoryBarrier.subresourceRange = subresourceRange; + + // Source layouts (old) + // Source access mask controls actions that have to be finished on the old layout + // before it will be transitioned to the new layout + switch (oldImageLayout) { + case VkImageLayout.Undefined: + // Image layout is undefined (or does not matter) + // Only valid as initial layout + // No flags required, listed only for completeness + imageMemoryBarrier.srcAccessMask = 0; + break; + + case VkImageLayout.Preinitialized: + // Image is preinitialized + // Only valid as initial layout for linear images, preserves memory contents + // Make sure host writes have been finished + imageMemoryBarrier.srcAccessMask = VkAccessFlags.HostWrite; + break; + + case VkImageLayout.ColorAttachmentOptimal: + // Image is a color attachment + // Make sure any writes to the color buffer have been finished + imageMemoryBarrier.srcAccessMask = VkAccessFlags.ColorAttachmentWrite; + break; + + case VkImageLayout.DepthStencilAttachmentOptimal: + // Image is a depth/stencil attachment + // Make sure any writes to the depth/stencil buffer have been finished + imageMemoryBarrier.srcAccessMask = VkAccessFlags.DepthStencilAttachmentWrite; + break; + + case VkImageLayout.TransferSrcOptimal: + // Image is a transfer source + // Make sure any reads from the image have been finished + imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferRead; + break; + + case VkImageLayout.TransferDstOptimal: + // Image is a transfer destination + // Make sure any writes to the image have been finished + imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferWrite; + break; + + case VkImageLayout.ShaderReadOnlyOptimal: + // Image is read by a shader + // Make sure any shader reads from the image have been finished + imageMemoryBarrier.srcAccessMask = VkAccessFlags.ShaderRead; + break; + } + + // Target layouts (new) + // Destination access mask controls the dependency for the new image layout + switch (newImageLayout) { + case VkImageLayout.TransferDstOptimal: + // Image will be used as a transfer destination + // Make sure any writes to the image have been finished + imageMemoryBarrier.dstAccessMask = VkAccessFlags.TransferWrite; + break; + + case VkImageLayout.TransferSrcOptimal: + // Image will be used as a transfer source + // Make sure any reads from and writes to the image have been finished + imageMemoryBarrier.srcAccessMask = imageMemoryBarrier.srcAccessMask | VkAccessFlags.TransferRead; + imageMemoryBarrier.dstAccessMask = VkAccessFlags.TransferRead; + break; + + case VkImageLayout.ColorAttachmentOptimal: + // Image will be used as a color attachment + // Make sure any writes to the color buffer have been finished + imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferRead; + imageMemoryBarrier.dstAccessMask = VkAccessFlags.ColorAttachmentWrite; + break; + + case VkImageLayout.DepthStencilAttachmentOptimal: + // Image layout will be used as a depth/stencil attachment + // Make sure any writes to depth/stencil buffer have been finished + imageMemoryBarrier.dstAccessMask = imageMemoryBarrier.dstAccessMask | VkAccessFlags.DepthStencilAttachmentWrite; + break; + + case VkImageLayout.ShaderReadOnlyOptimal: + // Image will be read in a shader (sampler, input attachment) + // Make sure any writes to the image have been finished + if (imageMemoryBarrier.srcAccessMask == 0) { + imageMemoryBarrier.srcAccessMask = VkAccessFlags.HostWrite | VkAccessFlags.TransferWrite; + } + imageMemoryBarrier.dstAccessMask = VkAccessFlags.ShaderRead; + break; + } + + // Put barrier inside setup command buffer + Vk.vkCmdPipelineBarrier ( + cmdbuffer, + srcStageMask, + dstStageMask, + 0, + 0, IntPtr.Zero, + 0, IntPtr.Zero, + 1, ref imageMemoryBarrier); + } + /// + /// Find usage flags and aspect flag from image layout + /// + public static void QueryLayoutRequirements (VkImageLayout layout, ref VkImageUsageFlags usage, ref VkImageAspectFlags aspectFlags) { + switch (layout) { + case VkImageLayout.ColorAttachmentOptimal: + case VkImageLayout.PresentSrcKHR: + case VkImageLayout.SharedPresentKHR: + aspectFlags |= VkImageAspectFlags.Color; + if (usage.HasFlag (VkImageUsageFlags.Sampled)) + usage |= VkImageUsageFlags.InputAttachment; + usage |= VkImageUsageFlags.ColorAttachment; + break; + case VkImageLayout.DepthStencilAttachmentOptimal: + aspectFlags |= VkImageAspectFlags.Depth | VkImageAspectFlags.Stencil; + usage |= VkImageUsageFlags.DepthStencilAttachment; + break; + case VkImageLayout.DepthStencilReadOnlyOptimal: + aspectFlags |= VkImageAspectFlags.Depth | VkImageAspectFlags.Stencil; + if (usage.HasFlag (VkImageUsageFlags.ColorAttachment)) + usage |= VkImageUsageFlags.InputAttachment; + else + usage |= VkImageUsageFlags.Sampled; + break; + case VkImageLayout.ShaderReadOnlyOptimal: + aspectFlags |= VkImageAspectFlags.Color; + usage |= VkImageUsageFlags.Sampled; + break; + case VkImageLayout.TransferSrcOptimal: + usage |= VkImageUsageFlags.TransferSrc; + break; + case VkImageLayout.TransferDstOptimal: + usage |= VkImageUsageFlags.TransferDst; + break; + case VkImageLayout.DepthReadOnlyStencilAttachmentOptimalKHR: + case VkImageLayout.DepthAttachmentStencilReadOnlyOptimalKHR: + aspectFlags |= VkImageAspectFlags.Depth | VkImageAspectFlags.Stencil; + usage |= VkImageUsageFlags.Sampled | VkImageUsageFlags.DepthStencilAttachment; + break; + } + } + /// + /// Try to get the block width and height of a compressed format + /// + /// truereturn true if format given as first argument is a compressed format. + /// Vulkan format to test. + /// Compressed block width. + /// Compressed block height. + public static bool TryGetCompressedFormatBlockSize (this VkFormat format, out uint width, out uint height) + { + width = height = 1; + if (format < VkFormat.Bc1RgbUnormBlock || format > (VkFormat)1000066013) //VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT) + return false; + if (format < VkFormat.Astc5x4UnormBlock) + width = height = 4; + else { + string str = format.ToString (); + if (str.StartsWith ("Astc", StringComparison.OrdinalIgnoreCase)) { + width = uint.Parse (str.Substring (4, 1)); + height = uint.Parse (str.Substring (6, 1)); + } + } + + return true; + } + //TODO:quick done list, refine needed + public static VkPipelineStageFlags GetDefaultStage (this VkImageLayout layout) { + switch (layout) { + case VkImageLayout.Preinitialized: + case VkImageLayout.Undefined: + return VkPipelineStageFlags.AllCommands; + + case VkImageLayout.General: + return VkPipelineStageFlags.ComputeShader; + + case VkImageLayout.ColorAttachmentOptimal: + case VkImageLayout.DepthStencilAttachmentOptimal: + return VkPipelineStageFlags.ColorAttachmentOutput; + + case VkImageLayout.DepthStencilReadOnlyOptimal: + case VkImageLayout.DepthReadOnlyStencilAttachmentOptimalKHR: + case VkImageLayout.DepthAttachmentStencilReadOnlyOptimalKHR: + return VkPipelineStageFlags.EarlyFragmentTests; + + case VkImageLayout.ShaderReadOnlyOptimal: + return VkPipelineStageFlags.FragmentShader; + + case VkImageLayout.TransferSrcOptimal: + case VkImageLayout.TransferDstOptimal: + return VkPipelineStageFlags.Transfer; + + case VkImageLayout.PresentSrcKHR: + case VkImageLayout.SharedPresentKHR: + return VkPipelineStageFlags.ColorAttachmentOutput; + + //case VkImageLayout.ShadingRateOptimalNV: + //case VkImageLayout.FragmentDensityMapOptimalEXT: + default: + return VkPipelineStageFlags.AllCommands; + } + } + public static Matrix4x4 CreatePerspectiveFieldOfView (float fov, float aspectRatio, float zNear, float zFar) { + float f = (float)(1.0 / System.Math.Tan (0.5 * fov)); + return new Matrix4x4 ( + f / aspectRatio, 0, 0, 0, + 0, -f, 0, 0, + 0, 0, zFar / (zNear - zFar), -1, + 0, 0, zNear * zFar / (zNear - zFar), 0 + ); + } + + public static VkShaderStageFlags ShaderKindToStageFlag (shaderc.ShaderKind shaderKind) { + switch (shaderKind) { + case shaderc.ShaderKind.VertexShader: + case shaderc.ShaderKind.GlslDefaultVertexShader: + return VkShaderStageFlags.Vertex; + case shaderc.ShaderKind.FragmentShader: + case shaderc.ShaderKind.GlslDefaultFragmentShader: + return VkShaderStageFlags.Fragment; + case shaderc.ShaderKind.ComputeShader: + case shaderc.ShaderKind.GlslDefaultComputeShader: + return VkShaderStageFlags.Compute; + case shaderc.ShaderKind.GeometryShader: + case shaderc.ShaderKind.GlslDefaultGeometryShader: + return VkShaderStageFlags.Geometry; + case shaderc.ShaderKind.TessControlShader: + case shaderc.ShaderKind.GlslDefaultTessControlShader: + return VkShaderStageFlags.TessellationControl; + case shaderc.ShaderKind.TessEvaluationShader: + case shaderc.ShaderKind.GlslDefaultTessEvaluationShader: + return VkShaderStageFlags.TessellationEvaluation; + case shaderc.ShaderKind.RaygenShader: + case shaderc.ShaderKind.GlslDefaultRaygenShader: + return VkShaderStageFlags.RaygenKHR; + case shaderc.ShaderKind.AnyhitShader: + case shaderc.ShaderKind.GlslDefaultAnyhitShader: + return VkShaderStageFlags.AnyHitKHR; + case shaderc.ShaderKind.ClosesthitShader: + case shaderc.ShaderKind.GlslDefaultClosesthitShader: + return VkShaderStageFlags.ClosestHitKHR; + case shaderc.ShaderKind.MissShader: + case shaderc.ShaderKind.GlslDefaultMissShader: + return VkShaderStageFlags.MissKHR; + case shaderc.ShaderKind.IntersectionShader: + case shaderc.ShaderKind.GlslDefaultIntersectionShader: + return VkShaderStageFlags.IntersectionKHR; + case shaderc.ShaderKind.CallableShader: + case shaderc.ShaderKind.GlslDefaultCallableShader: + return VkShaderStageFlags.CallableKHR; + case shaderc.ShaderKind.TaskShader: + case shaderc.ShaderKind.GlslDefaultTaskShader: + return VkShaderStageFlags.TaskNV; + case shaderc.ShaderKind.MeshShader: + case shaderc.ShaderKind.GlslDefaultMeshShader: + return VkShaderStageFlags.MeshNV; + default: + throw new NotSupportedException ($"shaderc shaderKind {shaderKind} conversion to VK StageFlag not handled"); + } + } + public static shaderc.ShaderKind ShaderStageToShaderKind (VkShaderStageFlags stageFlag) { + switch (stageFlag) { + case VkShaderStageFlags.Vertex: + return shaderc.ShaderKind.VertexShader; + case VkShaderStageFlags.TessellationControl: + return shaderc.ShaderKind.TessControlShader; + case VkShaderStageFlags.TessellationEvaluation: + return shaderc.ShaderKind.TessEvaluationShader; + case VkShaderStageFlags.Geometry: + return shaderc.ShaderKind.GeometryShader; + case VkShaderStageFlags.Fragment: + return shaderc.ShaderKind.FragmentShader; + case VkShaderStageFlags.Compute: + return shaderc.ShaderKind.ComputeShader; + case VkShaderStageFlags.RaygenKHR: + return shaderc.ShaderKind.RaygenShader; + case VkShaderStageFlags.AnyHitKHR: + return shaderc.ShaderKind.AnyhitShader; + case VkShaderStageFlags.ClosestHitKHR: + return shaderc.ShaderKind.ClosesthitShader; + case VkShaderStageFlags.MissKHR: + return shaderc.ShaderKind.MissShader; + case VkShaderStageFlags.IntersectionKHR: + return shaderc.ShaderKind.IntersectionShader; + case VkShaderStageFlags.CallableKHR: + return shaderc.ShaderKind.CallableShader; + case VkShaderStageFlags.TaskNV: + return shaderc.ShaderKind.TaskShader; + case VkShaderStageFlags.MeshNV: + return shaderc.ShaderKind.MeshShader; + default: + throw new NotSupportedException ($"Error Shader Stage flag conversion to ShaderKind: {stageFlag}"); + } + } + } +} diff --git a/vke/src/Utils.cs b/vke/src/Utils.cs deleted file mode 100644 index 86d026b..0000000 --- a/vke/src/Utils.cs +++ /dev/null @@ -1,482 +0,0 @@ -// Copyright (c) 2019-2022 Jean-Philippe Bruyère -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -using System; -using System.IO; -using System.Linq; -using System.Numerics; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Xml.Serialization; -using Vulkan; - -namespace vke { - public static partial class Helpers { - /// Throw an erro if VkResult != Success. - static void xmlMakeTypeFieldsAsAttributes (Type t, ref XmlAttributeOverrides overrides) - { - foreach (FieldInfo fi in t.GetFields (BindingFlags.Public | BindingFlags.Instance)) - overrides.Add (t, fi.Name, new XmlAttributes { XmlAttribute = new XmlAttributeAttribute () }); - } - public static XmlAttributeOverrides GetXmlOverrides () - { - XmlAttributeOverrides xmlAttributeOverrides = new XmlAttributeOverrides (); - //Assembly avk = Assembly.GetAssembly (typeof (VkInstance)); - xmlMakeTypeFieldsAsAttributes (typeof (VkDescriptorPoolSize), ref xmlAttributeOverrides); - return xmlAttributeOverrides; - } - static bool tryFindResource (Assembly a, string resId, out Stream stream) { - stream = null; - if (a == null) - return false; - stream = a.GetManifestResourceStream (resId); - return stream != null; - } - /// - /// Return a file or embedded resource stream. - /// - /// - /// Embedded resource path start with `#`. The entry assembly is always searched first to be able - /// to override resource defined in satellite assemblies. - /// To enforce assembly name selection, use `:` to split assembly and resource (ex; "Assembly:LogicalName"), - /// else assembly names will be determined with the first, or the two firsts names in the path string. - /// (ex: AssemblyName.filename.ext then Assembly.Name.filename.ext) - /// - /// The stream from path. - /// The file or stream path. Embedded resource path starts with '#' or contains ':'. - public static Stream GetStreamFromPath (string path) { - if (path.StartsWith("#", StringComparison.Ordinal)) - { - Stream stream = null; - string[] assemblyNames = null; - Assembly assembly = null; - - string resId = path.Substring(1); - - if (tryFindResource(Assembly.GetEntryAssembly(), resId, out stream)) - return stream; - - if (path.Contains(":", StringComparison.Ordinal)) { - assemblyNames = resId.Split (':'); - assembly = AppDomain.CurrentDomain.GetAssemblies ().FirstOrDefault (aa => aa.GetName ().Name == assemblyNames[0]); - if (assembly == null) - throw new Exception("Assembly not found: " + path); - if (tryFindResource(assembly, assemblyNames[1], out stream)) - return stream; - throw new Exception("Embedded resource not found in assembly: " + path); - } - - assemblyNames = resId.Split('.'); - assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(aa => aa.GetName().Name == assemblyNames[0]); - if (assembly == null && assemblyNames.Length > 3) - assembly = AppDomain.CurrentDomain.GetAssemblies() - .FirstOrDefault(aa => aa.GetName().Name == $"{assemblyNames[0]}.{assemblyNames[1]}"); - if (assembly != null && tryFindResource(assembly, resId, out stream)) - return stream; - throw new Exception("Resource not found: " + path); - } - if (!File.Exists (path)) - throw new FileNotFoundException ("File not found: ", path); - return new FileStream (path, FileMode.Open, FileAccess.Read); - } - /// Convert angle from degree to radian. - public static float DegreesToRadians (float degrees) { - return degrees * (float)Math.PI / 180f; - } - - /// - /// Populate a Vector3 with values from a float array - /// - public static void FromFloatArray (ref Vector3 v, float[] floats) { - v = Unsafe.As(ref floats)[0]; - } - /// - /// Populate a Vector4 with values from a float array - /// - public static void FromFloatArray (ref Vector4 v, float[] floats) { - MemoryMarshal.Cast(floats.AsSpan()); - } - /// - /// Populate a Quaternion with values from a float array - /// - public static void FromFloatArray (ref Quaternion q, float[] floats) { - q = Unsafe.As(ref floats)[0]; - } - /// - /// Populate a Vector2 with values from a byte array starting at offset - /// - public static void FromByteArray (ref Vector2 v, byte[] byteArray, int offset) { - v = Unsafe.As(ref Unsafe.AsRef(byteArray.SubArray(offset, 8)))[0]; - } - /// - /// Populate a Vector3 with values from a byte array starting at offset - /// - public static void FromByteArray (ref Vector3 v, byte[] byteArray, int offset) { - v = Unsafe.As(ref Unsafe.AsRef(byteArray.SubArray(offset, 12)))[0]; - } - /// - /// Populate a Vector4 with values from a byte array starting at offset - /// - public static void FromByteArray (ref Vector4 v, byte[] byteArray, int offset) { - v = Unsafe.As(ref Unsafe.AsRef(byteArray.SubArray(offset, 16)))[0]; - } - /// - /// Populate a Quaternion with values from a byte array starting at offset - /// - public static void FromByteArray (ref Quaternion v, byte[] byteArray, int offset) { - v = Unsafe.As(ref Unsafe.AsRef(byteArray.SubArray(offset, 16)))[0]; - } - - #region Extensions methods - public static void ImportFloatArray (this ref Vector3 v, float[] floats) { - v = Unsafe.As(ref floats)[0]; - } - public static Vector3 Transform (this Vector3 v, ref Matrix4x4 mat, bool translate = false) { - Vector4 v4 = Vector4.Transform (new Vector4 (v, translate ? 1f : 0f), mat); - return new Vector3 (v4.X, v4.Y, v4.Z); - } - public static Vector3 ToVector3 (this Vector4 v) { - return Unsafe.As(ref v); - } - public static T[] SubArray(this T[] array, int offset, int length) - { - T[] result = new T[length]; - Array.Copy(array, offset, result, 0, length); - return result; - } - #endregion - - // Fixed sub resource on first mip level and layer - public static void setImageLayout ( - VkCommandBuffer cmdbuffer, - VkImage image, - VkImageAspectFlags aspectMask, - VkImageLayout oldImageLayout, - VkImageLayout newImageLayout, - VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands, - VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands) { - VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange { - aspectMask = aspectMask, - baseMipLevel = 0, - levelCount = 1, - layerCount = 1, - }; - setImageLayout (cmdbuffer, image, aspectMask, oldImageLayout, newImageLayout, subresourceRange); - } - - // Create an image memory barrier for changing the layout of - // an image and put it into an active command buffer - // See chapter 11.4 "Image Layout" for details - - public static void setImageLayout ( - VkCommandBuffer cmdbuffer, - VkImage image, - VkImageAspectFlags aspectMask, - VkImageLayout oldImageLayout, - VkImageLayout newImageLayout, - VkImageSubresourceRange subresourceRange, - VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands, - VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands) { - // Create an image barrier object - VkImageMemoryBarrier imageMemoryBarrier = default; - imageMemoryBarrier.srcQueueFamilyIndex = Vk.QueueFamilyIgnored; - imageMemoryBarrier.dstQueueFamilyIndex = Vk.QueueFamilyIgnored; - imageMemoryBarrier.oldLayout = oldImageLayout; - imageMemoryBarrier.newLayout = newImageLayout; - imageMemoryBarrier.image = image; - imageMemoryBarrier.subresourceRange = subresourceRange; - - // Source layouts (old) - // Source access mask controls actions that have to be finished on the old layout - // before it will be transitioned to the new layout - switch (oldImageLayout) { - case VkImageLayout.Undefined: - // Image layout is undefined (or does not matter) - // Only valid as initial layout - // No flags required, listed only for completeness - imageMemoryBarrier.srcAccessMask = 0; - break; - - case VkImageLayout.Preinitialized: - // Image is preinitialized - // Only valid as initial layout for linear images, preserves memory contents - // Make sure host writes have been finished - imageMemoryBarrier.srcAccessMask = VkAccessFlags.HostWrite; - break; - - case VkImageLayout.ColorAttachmentOptimal: - // Image is a color attachment - // Make sure any writes to the color buffer have been finished - imageMemoryBarrier.srcAccessMask = VkAccessFlags.ColorAttachmentWrite; - break; - - case VkImageLayout.DepthStencilAttachmentOptimal: - // Image is a depth/stencil attachment - // Make sure any writes to the depth/stencil buffer have been finished - imageMemoryBarrier.srcAccessMask = VkAccessFlags.DepthStencilAttachmentWrite; - break; - - case VkImageLayout.TransferSrcOptimal: - // Image is a transfer source - // Make sure any reads from the image have been finished - imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferRead; - break; - - case VkImageLayout.TransferDstOptimal: - // Image is a transfer destination - // Make sure any writes to the image have been finished - imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferWrite; - break; - - case VkImageLayout.ShaderReadOnlyOptimal: - // Image is read by a shader - // Make sure any shader reads from the image have been finished - imageMemoryBarrier.srcAccessMask = VkAccessFlags.ShaderRead; - break; - } - - // Target layouts (new) - // Destination access mask controls the dependency for the new image layout - switch (newImageLayout) { - case VkImageLayout.TransferDstOptimal: - // Image will be used as a transfer destination - // Make sure any writes to the image have been finished - imageMemoryBarrier.dstAccessMask = VkAccessFlags.TransferWrite; - break; - - case VkImageLayout.TransferSrcOptimal: - // Image will be used as a transfer source - // Make sure any reads from and writes to the image have been finished - imageMemoryBarrier.srcAccessMask = imageMemoryBarrier.srcAccessMask | VkAccessFlags.TransferRead; - imageMemoryBarrier.dstAccessMask = VkAccessFlags.TransferRead; - break; - - case VkImageLayout.ColorAttachmentOptimal: - // Image will be used as a color attachment - // Make sure any writes to the color buffer have been finished - imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferRead; - imageMemoryBarrier.dstAccessMask = VkAccessFlags.ColorAttachmentWrite; - break; - - case VkImageLayout.DepthStencilAttachmentOptimal: - // Image layout will be used as a depth/stencil attachment - // Make sure any writes to depth/stencil buffer have been finished - imageMemoryBarrier.dstAccessMask = imageMemoryBarrier.dstAccessMask | VkAccessFlags.DepthStencilAttachmentWrite; - break; - - case VkImageLayout.ShaderReadOnlyOptimal: - // Image will be read in a shader (sampler, input attachment) - // Make sure any writes to the image have been finished - if (imageMemoryBarrier.srcAccessMask == 0) { - imageMemoryBarrier.srcAccessMask = VkAccessFlags.HostWrite | VkAccessFlags.TransferWrite; - } - imageMemoryBarrier.dstAccessMask = VkAccessFlags.ShaderRead; - break; - } - - // Put barrier inside setup command buffer - Vk.vkCmdPipelineBarrier ( - cmdbuffer, - srcStageMask, - dstStageMask, - 0, - 0, IntPtr.Zero, - 0, IntPtr.Zero, - 1, ref imageMemoryBarrier); - } - /// - /// Find usage flags and aspect flag from image layout - /// - public static void QueryLayoutRequirements (VkImageLayout layout, ref VkImageUsageFlags usage, ref VkImageAspectFlags aspectFlags) { - switch (layout) { - case VkImageLayout.ColorAttachmentOptimal: - case VkImageLayout.PresentSrcKHR: - case VkImageLayout.SharedPresentKHR: - aspectFlags |= VkImageAspectFlags.Color; - if (usage.HasFlag (VkImageUsageFlags.Sampled)) - usage |= VkImageUsageFlags.InputAttachment; - usage |= VkImageUsageFlags.ColorAttachment; - break; - case VkImageLayout.DepthStencilAttachmentOptimal: - aspectFlags |= VkImageAspectFlags.Depth | VkImageAspectFlags.Stencil; - usage |= VkImageUsageFlags.DepthStencilAttachment; - break; - case VkImageLayout.DepthStencilReadOnlyOptimal: - aspectFlags |= VkImageAspectFlags.Depth | VkImageAspectFlags.Stencil; - if (usage.HasFlag (VkImageUsageFlags.ColorAttachment)) - usage |= VkImageUsageFlags.InputAttachment; - else - usage |= VkImageUsageFlags.Sampled; - break; - case VkImageLayout.ShaderReadOnlyOptimal: - aspectFlags |= VkImageAspectFlags.Color; - usage |= VkImageUsageFlags.Sampled; - break; - case VkImageLayout.TransferSrcOptimal: - usage |= VkImageUsageFlags.TransferSrc; - break; - case VkImageLayout.TransferDstOptimal: - usage |= VkImageUsageFlags.TransferDst; - break; - case VkImageLayout.DepthReadOnlyStencilAttachmentOptimalKHR: - case VkImageLayout.DepthAttachmentStencilReadOnlyOptimalKHR: - aspectFlags |= VkImageAspectFlags.Depth | VkImageAspectFlags.Stencil; - usage |= VkImageUsageFlags.Sampled | VkImageUsageFlags.DepthStencilAttachment; - break; - } - } - /// - /// Try to get the block width and height of a compressed format - /// - /// truereturn true if format given as first argument is a compressed format. - /// Vulkan format to test. - /// Compressed block width. - /// Compressed block height. - public static bool TryGetCompressedFormatBlockSize (this VkFormat format, out uint width, out uint height) - { - width = height = 1; - if (format < VkFormat.Bc1RgbUnormBlock || format > (VkFormat)1000066013) //VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT) - return false; - if (format < VkFormat.Astc5x4UnormBlock) - width = height = 4; - else { - string str = format.ToString (); - if (str.StartsWith ("Astc", StringComparison.OrdinalIgnoreCase)) { - width = uint.Parse (str.Substring (4, 1)); - height = uint.Parse (str.Substring (6, 1)); - } - } - - return true; - } - //TODO:quick done list, refine needed - public static VkPipelineStageFlags GetDefaultStage (this VkImageLayout layout) { - switch (layout) { - case VkImageLayout.Preinitialized: - case VkImageLayout.Undefined: - return VkPipelineStageFlags.AllCommands; - - case VkImageLayout.General: - return VkPipelineStageFlags.ComputeShader; - - case VkImageLayout.ColorAttachmentOptimal: - case VkImageLayout.DepthStencilAttachmentOptimal: - return VkPipelineStageFlags.ColorAttachmentOutput; - - case VkImageLayout.DepthStencilReadOnlyOptimal: - case VkImageLayout.DepthReadOnlyStencilAttachmentOptimalKHR: - case VkImageLayout.DepthAttachmentStencilReadOnlyOptimalKHR: - return VkPipelineStageFlags.EarlyFragmentTests; - - case VkImageLayout.ShaderReadOnlyOptimal: - return VkPipelineStageFlags.FragmentShader; - - case VkImageLayout.TransferSrcOptimal: - case VkImageLayout.TransferDstOptimal: - return VkPipelineStageFlags.Transfer; - - case VkImageLayout.PresentSrcKHR: - case VkImageLayout.SharedPresentKHR: - return VkPipelineStageFlags.ColorAttachmentOutput; - - //case VkImageLayout.ShadingRateOptimalNV: - //case VkImageLayout.FragmentDensityMapOptimalEXT: - default: - return VkPipelineStageFlags.AllCommands; - } - } - public static Matrix4x4 CreatePerspectiveFieldOfView (float fov, float aspectRatio, float zNear, float zFar) { - float f = (float)(1.0 / System.Math.Tan (0.5 * fov)); - return new Matrix4x4 ( - f / aspectRatio, 0, 0, 0, - 0, -f, 0, 0, - 0, 0, zFar / (zNear - zFar), -1, - 0, 0, zNear * zFar / (zNear - zFar), 0 - ); - } - - public static VkShaderStageFlags ShaderKindToStageFlag (shaderc.ShaderKind shaderKind) { - switch (shaderKind) { - case shaderc.ShaderKind.VertexShader: - case shaderc.ShaderKind.GlslDefaultVertexShader: - return VkShaderStageFlags.Vertex; - case shaderc.ShaderKind.FragmentShader: - case shaderc.ShaderKind.GlslDefaultFragmentShader: - return VkShaderStageFlags.Fragment; - case shaderc.ShaderKind.ComputeShader: - case shaderc.ShaderKind.GlslDefaultComputeShader: - return VkShaderStageFlags.Compute; - case shaderc.ShaderKind.GeometryShader: - case shaderc.ShaderKind.GlslDefaultGeometryShader: - return VkShaderStageFlags.Geometry; - case shaderc.ShaderKind.TessControlShader: - case shaderc.ShaderKind.GlslDefaultTessControlShader: - return VkShaderStageFlags.TessellationControl; - case shaderc.ShaderKind.TessEvaluationShader: - case shaderc.ShaderKind.GlslDefaultTessEvaluationShader: - return VkShaderStageFlags.TessellationEvaluation; - case shaderc.ShaderKind.RaygenShader: - case shaderc.ShaderKind.GlslDefaultRaygenShader: - return VkShaderStageFlags.RaygenKHR; - case shaderc.ShaderKind.AnyhitShader: - case shaderc.ShaderKind.GlslDefaultAnyhitShader: - return VkShaderStageFlags.AnyHitKHR; - case shaderc.ShaderKind.ClosesthitShader: - case shaderc.ShaderKind.GlslDefaultClosesthitShader: - return VkShaderStageFlags.ClosestHitKHR; - case shaderc.ShaderKind.MissShader: - case shaderc.ShaderKind.GlslDefaultMissShader: - return VkShaderStageFlags.MissKHR; - case shaderc.ShaderKind.IntersectionShader: - case shaderc.ShaderKind.GlslDefaultIntersectionShader: - return VkShaderStageFlags.IntersectionKHR; - case shaderc.ShaderKind.CallableShader: - case shaderc.ShaderKind.GlslDefaultCallableShader: - return VkShaderStageFlags.CallableKHR; - case shaderc.ShaderKind.TaskShader: - case shaderc.ShaderKind.GlslDefaultTaskShader: - return VkShaderStageFlags.TaskNV; - case shaderc.ShaderKind.MeshShader: - case shaderc.ShaderKind.GlslDefaultMeshShader: - return VkShaderStageFlags.MeshNV; - default: - throw new NotSupportedException ($"shaderc shaderKind {shaderKind} conversion to VK StageFlag not handled"); - } - } - public static shaderc.ShaderKind ShaderStageToShaderKind (VkShaderStageFlags stageFlag) { - switch (stageFlag) { - case VkShaderStageFlags.Vertex: - return shaderc.ShaderKind.VertexShader; - case VkShaderStageFlags.TessellationControl: - return shaderc.ShaderKind.TessControlShader; - case VkShaderStageFlags.TessellationEvaluation: - return shaderc.ShaderKind.TessEvaluationShader; - case VkShaderStageFlags.Geometry: - return shaderc.ShaderKind.GeometryShader; - case VkShaderStageFlags.Fragment: - return shaderc.ShaderKind.FragmentShader; - case VkShaderStageFlags.Compute: - return shaderc.ShaderKind.ComputeShader; - case VkShaderStageFlags.RaygenKHR: - return shaderc.ShaderKind.RaygenShader; - case VkShaderStageFlags.AnyHitKHR: - return shaderc.ShaderKind.AnyhitShader; - case VkShaderStageFlags.ClosestHitKHR: - return shaderc.ShaderKind.ClosesthitShader; - case VkShaderStageFlags.MissKHR: - return shaderc.ShaderKind.MissShader; - case VkShaderStageFlags.IntersectionKHR: - return shaderc.ShaderKind.IntersectionShader; - case VkShaderStageFlags.CallableKHR: - return shaderc.ShaderKind.CallableShader; - case VkShaderStageFlags.TaskNV: - return shaderc.ShaderKind.TaskShader; - case VkShaderStageFlags.MeshNV: - return shaderc.ShaderKind.MeshShader; - default: - throw new NotSupportedException ($"Error Shader Stage flag conversion to ShaderKind: {stageFlag}"); - } - } - } -}