private static void GenerateHelperCommands(CppCompilation compilation, string outputPath)
        {
            // Generate Functions
            using var writer = new CodeWriter(Path.Combine(outputPath, "VkHelpers.cs"),
                                              "System",
                                              "System.Diagnostics",
                                              "System.Runtime.InteropServices",
                                              "Vortice.Mathematics");

            using (writer.PushBlock($"unsafe partial class Vulkan"))
            {
                // Generate methods with array calls
                foreach (var function in compilation.Functions)
                {
                    if (!s_outArrayReturnFunctions.Contains(function.Name))
                    {
                        continue;
                    }

                    if (function.Name == "vkGetPhysicalDeviceSurfacePresentModesKHR")
                    {
                    }

                    // Find count and array return type.
                    var countParameterName      = string.Empty;
                    var returnArrayTypeName     = string.Empty;
                    var returnVariableName      = string.Empty;
                    var newParameters           = new List <CppParameter>();
                    var hasArrayReturn          = false;
                    var countArgumentArrayIndex = 0;

                    foreach (var parameter in function.Parameters)
                    {
                        if (parameter.Name.EndsWith("count", StringComparison.OrdinalIgnoreCase))
                        {
                            countParameterName = GetParameterName(parameter.Name);
                            continue;
                        }

                        if (CanBeUsedAsOutput(parameter.Type, out var cppTypeDeclaration))
                        {
                            returnVariableName      = GetParameterName(parameter.Name);
                            returnArrayTypeName     = GetCsTypeName(cppTypeDeclaration);
                            hasArrayReturn          = true;
                            countArgumentArrayIndex = function.Parameters.IndexOf(parameter) - 1;
                            continue;
                        }

                        if (parameter.Type is CppPointerType pointerType &&
                            pointerType.ElementType is CppQualifiedType qualifiedType &&
                            !string.IsNullOrEmpty(countParameterName))
                        {
                            returnVariableName      = GetParameterName(parameter.Name);
                            returnArrayTypeName     = GetCsTypeName(qualifiedType);
                            hasArrayReturn          = false;
                            countArgumentArrayIndex = function.Parameters.IndexOf(parameter) - 1;
                            continue;
                        }

                        newParameters.Add(parameter);
                    }

                    var csCountParameterType = "uint";
                    if (!hasArrayReturn)
                    {
                        // Calls without return array.
                        var returnType = GetCsTypeName(function.ReturnType);

                        var argumentsSingleElementBuilder = new StringBuilder();
                        var argumentsReadOnlySpanBuilder  = new StringBuilder();
                        var index = 0;

                        var invokeSingleElementParameters = new List <string>();
                        var invokeElementsParameters      = new List <string>();

                        foreach (var cppParameter in newParameters)
                        {
                            var paramCsTypeName   = GetCsTypeName(cppParameter.Type, false);
                            var paramCsName       = GetParameterName(cppParameter.Name);
                            var argumentSignature = $"{paramCsTypeName} {paramCsName}";

                            if (index == countArgumentArrayIndex)
                            {
                                AppendCountParameter(false,
                                                     argumentsSingleElementBuilder, argumentsReadOnlySpanBuilder,
                                                     invokeSingleElementParameters, invokeElementsParameters,
                                                     returnArrayTypeName, returnVariableName,
                                                     csCountParameterType);
                            }

                            argumentsSingleElementBuilder.Append(argumentSignature);
                            argumentsReadOnlySpanBuilder.Append(argumentSignature);
                            if (index < newParameters.Count - 1)
                            {
                                argumentsSingleElementBuilder.Append(", ");
                                argumentsReadOnlySpanBuilder.Append(", ");
                            }

                            invokeSingleElementParameters.Add(paramCsName);
                            invokeElementsParameters.Add(paramCsName);
                            index++;
                        }

                        // Functions like vkFlushMappedMemoryRanges
                        if (newParameters.Count == countArgumentArrayIndex)
                        {
                            AppendCountParameter(true,
                                                 argumentsSingleElementBuilder, argumentsReadOnlySpanBuilder,
                                                 invokeSingleElementParameters, invokeElementsParameters,
                                                 returnArrayTypeName, returnVariableName,
                                                 csCountParameterType);
                        }

                        // Single element function.
                        var argumentsSingleElementString = argumentsSingleElementBuilder.ToString();
                        using (writer.PushBlock($"public static {returnType} {function.Name}({argumentsSingleElementString})"))
                        {
                            if (returnType != "void")
                            {
                                writer.Write("return ");
                            }

                            EmitInvoke(writer, function, invokeSingleElementParameters, false);
                        }

                        writer.WriteLine();

                        // ReadOnlySpan
                        var argumentsReadOnlySpanString = argumentsReadOnlySpanBuilder.ToString();
                        using (writer.PushBlock($"public static {returnType} {function.Name}({argumentsReadOnlySpanString})"))
                        {
                            using (writer.PushBlock($"fixed ({returnArrayTypeName}* {returnVariableName}Ptr = {returnVariableName})"))
                            {
                                if (returnType != "void")
                                {
                                    writer.Write("return ");
                                }

                                EmitInvoke(writer, function, invokeElementsParameters, false);
                            }
                        }
                    }
                    else
                    {
                        var argumentsString = GetParameterSignature(newParameters, false);
                        var returnType      = $"ReadOnlySpan<{returnArrayTypeName}>";

                        using (writer.PushBlock($"public static {returnType} {function.Name}({argumentsString})"))
                        {
                            //var csCountParameterType = GetCsTypeName(countParameterType);
                            writer.WriteLine($"{csCountParameterType} {countParameterName} = 0;");

                            var invokeParameters = new List <string>(newParameters.Select(item => GetParameterName(item.Name)))
                            {
                                $"&{countParameterName}",
                                "null"
                            };

                            EmitInvoke(writer, function, invokeParameters);

                            writer.WriteLine();
                            // Alloc array.
                            writer.WriteLine($"{returnType} {returnVariableName} = new {returnArrayTypeName}[{countParameterName}];");

                            // Write fixed access
                            using (writer.PushBlock($"fixed ({returnArrayTypeName}* {returnVariableName}Ptr = {returnVariableName})"))
                            {
                                invokeParameters[invokeParameters.Count - 1] = $"{returnVariableName}Ptr";
                                EmitInvoke(writer, function, invokeParameters);
                            }

                            writer.WriteLine($"return {returnVariableName};");
                        }
                    }

                    writer.WriteLine();
                }
            }
        }
        private static void GenerateCommands(CppCompilation compilation, string outputPath)
        {
            // Generate Functions
            using var writer = new CodeWriter(Path.Combine(outputPath, "Commands.cs"),
                                              "System",
                                              "System.Diagnostics",
                                              "System.Runtime.InteropServices",
                                              "Vortice.Mathematics");

            var commands         = new Dictionary <string, CppFunction>();
            var instanceCommands = new Dictionary <string, CppFunction>();
            var deviceCommands   = new Dictionary <string, CppFunction>();

            foreach (var cppFunction in compilation.Functions)
            {
                var  returnType      = GetCsTypeName(cppFunction.ReturnType, false);
                bool canUseOut       = s_outReturnFunctions.Contains(cppFunction.Name);
                var  csName          = cppFunction.Name;
                var  argumentsString = GetParameterSignature(cppFunction, canUseOut);

                if (disableCalliFunction)
                {
                    writer.WriteLine("[UnmanagedFunctionPointer(CallingConvention.StdCall)]");
                    writer.WriteLine($"public unsafe delegate {returnType} {csName}Delegate({argumentsString});");
                    writer.WriteLine();
                }

                commands.Add(csName, cppFunction);

                if (cppFunction.Parameters.Count > 0)
                {
                    var firstParameter = cppFunction.Parameters[0];
                    if (firstParameter.Type is CppTypedef typedef)
                    {
                        if (typedef.Name == "VkInstance" ||
                            typedef.Name == "VkPhysicalDevice" ||
                            IsInstanceFunction(cppFunction.Name))
                        {
                            instanceCommands.Add(csName, cppFunction);
                        }
                        else
                        {
                            deviceCommands.Add(csName, cppFunction);
                        }
                    }
                }
            }

            using (writer.PushBlock($"unsafe partial class Vulkan"))
            {
                foreach (var command in commands)
                {
                    var cppFunction = command.Value;

                    if (disableCalliFunction)
                    {
                        writer.WriteLine($"private static {command.Key}Delegate {command.Key}_ptr;");
                    }
                    else
                    {
                        writer.WriteLine($"private static IntPtr {command.Key}_ptr;");
                        writer.WriteLine($"[Calli]");
                    }

                    var  returnType      = GetCsTypeName(cppFunction.ReturnType, false);
                    bool canUseOut       = s_outReturnFunctions.Contains(cppFunction.Name);
                    var  argumentsString = GetParameterSignature(cppFunction, canUseOut);

                    using (writer.PushBlock($"public static {returnType} {cppFunction.Name}({argumentsString})"))
                    {
                        if (disableCalliFunction)
                        {
                            if (returnType != "void")
                            {
                                writer.Write("return ");
                            }

                            writer.Write($"{command.Key}_ptr(");
                            var index = 0;
                            foreach (var cppParameter in cppFunction.Parameters)
                            {
                                var paramCsName = GetParameterName(cppParameter.Name);
                                if (canUseOut && CanBeUsedAsOutput(cppParameter.Type, out var cppTypeDeclaration))
                                {
                                    writer.Write("out ");
                                }

                                writer.Write($"{paramCsName}");
                                if (index < cppFunction.Parameters.Count - 1)
                                {
                                    writer.Write(", ");
                                }

                                index++;
                            }

                            writer.WriteLine($");");
                        }
                        else
                        {
                            writer.WriteLine("throw new NotImplementedException();");
                        }
                    }
                    writer.WriteLine();
                }

                WriteCommands(writer, "GenLoadInstance", instanceCommands);
                WriteCommands(writer, "GenLoadDevice", deviceCommands);
            }
        }