public async Task GenerateEnumTypeHeader()
        {
            string enumType  = null;
            int    numValues = Registry.Functions.Count;

            if (numValues < byte.MaxValue)
            {
                enumType = "uint8_t";
            }
            else if (numValues < ushort.MaxValue)
            {
                enumType = "uint16_t";
            }
            else if ((uint)numValues < uint.MaxValue)
            {
                enumType = "uint32_t";
            }

            using (var header = CreateHeader("gl_function_enums.hpp"))
            {
                var context = new CodegenContext(header);
                await context.EmitLine("#pragma once");

                await context.EmitLine("#include <stdint.h>");

                if (Registry.EnumTypes.Count > 0)
                {
                    await context.EmitLine("#include <glad/glad.h>");
                }
                context.EmitLine();

                await context.EmitLine("namespace multigl");

                await context.EmitScope(async() =>
                {
                    await context.EmitLine($"typedef {enumType} gl_command_id_t;");
                    await context.EmitLine("namespace CommandIdEnum");
                    await context.EmitScope(async() =>
                    {
                        await context.EmitEnum("Enum : gl_command_id_t", async() =>
                        {
                            foreach (var v in Registry.Functions)
                            {
                                await context.EmitLine($"{v.Value.NoGLName},");
                            }
                            await context.EmitLine("Count");
                        });
                    });
                    await context.EmitLine("typedef CommandIdEnum::Enum CommandId;");

                    foreach (var enumType in Registry.EnumTypes)
                    {
                        context.EmitLine();
                        await context.EmitLine($"namespace {enumType.Value.Name}Enum");
                        await context.EmitScope(async() => {
                            await context.EmitEnum("Enum : GLenum", async() => {
                                int cur = 0;
                                int cnt = enumType.Value.Values.Count;
                                foreach (var v in enumType.Value.Values)
                                {
                                    context.EmitIndent();
                                    await context.Emit($"{v.Key} = {v.Value}");
                                    ++cur;
                                    if (cur < cnt)
                                    {
                                        await context.Emit(",");
                                    }
                                    context.EmitLineUnindented();
                                }
                            });
                        });
                        await context.EmitLine($"typedef {enumType.Value.Name}Enum::Enum {enumType.Value.Name};");
                    }
                });
            }
        }
        public async Task GenerateCommandBufferHeader(string bufferClass, string fileName, List <KeyValuePair <string, FunctionEntry> > functionList)
        {
            using (var header = CreateHeader(fileName))
            {
                var context = new CodegenContext(header);
                await context.EmitLine("#pragma once");

                await context.EmitLine("#include <glad/glad.h>");

                await context.EmitLine("#include \"gl_resource_manager.hpp\"");

                await context.EmitLine("#include \"raw_rw_buffer.hpp\"");

                if (Registry.EnumTypes.Count > 0)
                {
                    await context.EmitLine("#include \"gl_function_enums.hpp\"");
                }

                context.EmitLine();
                await context.EmitLine("namespace multigl");

                await context.EmitScope(async() =>
                {
                    await context.EmitClass(bufferClass, async() =>
                    {
                        await context.EmitStructAccess("public");
                        await context.EmitLine($"{bufferClass}(ResourceManager& manager);");
                        await context.EmitLine($"~{bufferClass}();");
                        context.EmitLine();

                        await context.EmitStructAccess("public");

                        var accessTracker = context.CreateAccessTracker("public");
                        foreach (var fk in functionList)
                        {
                            var function = fk.Value;
                            await accessTracker.WriteAccess(function.Access);

                            var noGLName = function.NoGLName;

                            if (!function.ShouldReturnAsFinalArgPointer)
                            {
                                context.EmitIndent();
                                await context.Emit($"{function.Type.ReturnType} {noGLName}(");
                                for (int i = 0; i < function.Type.Arguments.Count; ++i)
                                {
                                    var arg      = function.Type.Arguments[i];
                                    var fullType = $"{(arg.IsEnumType ? "multigl::" : "")}{arg.Type}";
                                    await context.Emit($"{fullType} {arg.Name}");
                                    if (i < function.Type.Arguments.Count - 1)
                                    {
                                        await context.Emit(", ");
                                    }
                                }
                                await context.EmitLineUnindented(");");
                            }
                            else
                            {
                                context.EmitIndent();
                                await context.Emit($"void {noGLName}(");
                                for (int i = 0; i < function.Type.Arguments.Count; ++i)
                                {
                                    var arg = function.Type.Arguments[i];
                                    await context.Emit($"{arg.Type} {arg.Name}");
                                    await context.Emit(", ");
                                }
                                await context.Emit($"{function.Type.ReturnType}* returnVal = 0");
                                await context.EmitLineUnindented(");");
                            }
                        }
                        context.EmitLine();

                        await context.EmitStructAccess("public");
                        await context.EmitLine("void ProcessCommands();");
                        context.EmitLine();

                        await context.EmitStructAccess("private");
                        await context.EmitLine($"ResourceManager& {ResourceManager};");
                        await context.EmitLine($"raw_rw_buffer {DataBuffer};");
                        if (bufferClass == ImmediateBufferClass)
                        {
                            await context.EmitLine("bool m_HasSubmittedWork;");
                        }
                    });
                });
            }
        }
        public async Task GenerateCommandBufferReadSource(string bufferClass, string fileName, List <KeyValuePair <string, FunctionEntry> > functionList)
        {
            using (var source = CreateSourceFile(fileName))
            {
                var context = new CodegenContext(source);
                await context.EmitLine($"#include <{(bufferClass == CommandBufferClass ? "gl_command_buffer.hpp" : "gl_immediate_buffer.hpp")}>");

                await context.EmitLine("#include <gl_utilities.hpp>");

                context.EmitLine();

                await context.EmitLine("using namespace multigl;");

                await context.EmitLine($"void {bufferClass}::ProcessCommands()");

                await context.EmitScope(async() =>
                {
                    await context.EmitLine($"while({DataBuffer}.has_commands())");
                    await context.EmitScope(async() =>
                    {
                        await context.EmitLine($"auto cmd = {DataBuffer}.read_command();");
                        await context.EmitLine("switch(cmd)");
                        await context.EmitScope(async() =>
                        {
                            foreach (var fk in functionList)
                            {
                                var overrideList = Tracker.GetOverrideList(fk.Key);
                                var function     = fk.Value;
                                await context.EmitLine($"case CommandId::{function.NoGLName}:");
                                await context.EmitScope(async() => {
                                    Func <Task> defaultReadFunc = async() =>
                                    {
                                        if (!function.ShouldReturnAsFinalArgPointer)
                                        {
                                            var args = function.Type.Arguments;
                                            for (int i = 0; i < args.Count; ++i)
                                            {
                                                var arg             = args[i];
                                                var argReadOverride = Tracker.GetArgumentTypeReadOverride(arg.Type);
                                                if (argReadOverride != null)
                                                {
                                                    await argReadOverride(context, function, arg);
                                                }
                                                else
                                                {
                                                    var fullType = $"{(arg.IsEnumType ? "multigl::" : "")}{arg.Type}";
                                                    await context.EmitLine($"{fullType} {arg.Name} = {DataBuffer}.read<{fullType}>();");
                                                }
                                            }
                                        }
                                        else
                                        {
                                            var args = function.Type.Arguments;
                                            for (int i = 0; i < args.Count; ++i)
                                            {
                                                var arg             = args[i];
                                                var argReadOverride = Tracker.GetArgumentTypeReadOverride(arg.Type);
                                                if (argReadOverride != null)
                                                {
                                                    await argReadOverride(context, function, arg);
                                                }
                                                else
                                                {
                                                    var fullType = $"{(arg.IsEnumType ? "multigl::" : "")}{arg.Type}";
                                                    await context.EmitLine($"{arg.Type} {arg.Name} = {DataBuffer}.read<{fullType}>();");
                                                }
                                            }

                                            {
                                                var fullType = $"{(function.Type.IsReturnEnum ? "multigl::" : "")}{function.Type.ReturnType}";
                                                await context.EmitLine($"auto returnVal = {DataBuffer}.read<{fullType}*>();");
                                            }
                                            await context.EmitLine("if (returnVal)");
                                            await context.EmitScope(async() => {
                                                context.EmitIndent();

                                                if (function.Type.IsReturnEnum)
                                                {
                                                    await context.Emit($"GL_CHECK(*returnVal = {function.Type.ReturnType}({function.Name}(");
                                                }
                                                else
                                                {
                                                    await context.Emit($"GL_CHECK(*returnVal = ({function.Name}(");
                                                }
                                                for (int i = 0; i < args.Count; ++i)
                                                {
                                                    var arg = args[i];
                                                    await context.Emit($"{arg.Name}");
                                                    if (i < args.Count - 1)
                                                    {
                                                        await context.Emit(", ");
                                                    }
                                                }

                                                await context.EmitLineUnindented($")));");
                                                await context.EmitLine("return;");
                                            });
                                        }
                                    };

                                    if (overrideList == null || overrideList.Count == 0)
                                    {
                                        var args = function.Type.Arguments;
                                        await defaultReadFunc();
                                        context.EmitIndent();
                                        await context.Emit($"GL_CHECK({function.Name}(");

                                        for (int i = 0; i < args.Count; ++i)
                                        {
                                            var arg = args[i];
                                            await context.Emit($"{arg.Name}");
                                            if (i < args.Count - 1)
                                            {
                                                await context.Emit(", ");
                                            }
                                        }

                                        await context.EmitLineUnindented($"));");
                                    }
                                    else
                                    {
                                        bool hasRun = false;
                                        foreach (var v in overrideList)
                                        {
                                            if (v.ModifyReadFunction != null)
                                            {
                                                await v.ModifyReadFunction(context, defaultReadFunc, function);
                                                hasRun = true;
                                            }
                                        }

                                        if (!hasRun)
                                        {
                                            var args = function.Type.Arguments;
                                            await defaultReadFunc();
                                            context.EmitIndent();
                                            await context.Emit($"GL_CHECK({function.Name}(");

                                            for (int i = 0; i < args.Count; ++i)
                                            {
                                                var arg = args[i];
                                                await context.Emit($"{arg.Name}");
                                                if (i < args.Count - 1)
                                                {
                                                    await context.Emit(", ");
                                                }
                                            }

                                            await context.EmitLineUnindented($"));");
                                        }
                                    }
                                    await context.EmitLine($"break;");
                                });
                            }
                        });
                    });
                    await context.EmitLine($"{DataBuffer}.reset();");
                });
            }
        }