From cd5c0269b0df5d6453ca02487103606ae7db1776 Mon Sep 17 00:00:00 2001 From: giawa Date: Tue, 18 Jun 2019 14:01:19 -0700 Subject: [PATCH 1/4] Add support for parameters having variant types as defined in variants.json (optional). This will cause a FunctionDefinition to expand to have several OverloadDefinitions if variants are found, and will cause both native methods and their wrappers to be generated for the variants. --- src/CodeGenerator/Program.cs | 102 +++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 5 deletions(-) diff --git a/src/CodeGenerator/Program.cs b/src/CodeGenerator/Program.cs index 18ddf86..f3e1441 100644 --- a/src/CodeGenerator/Program.cs +++ b/src/CodeGenerator/Program.cs @@ -130,6 +130,16 @@ namespace CodeGenerator functionsJson = JObject.Load(jr); } + JObject variantsJson = null; + if (File.Exists(Path.Combine(AppContext.BaseDirectory, "variants.json"))) + { + using (StreamReader fs = File.OpenText(Path.Combine(AppContext.BaseDirectory, "variants.json"))) + using (JsonTextReader jr = new JsonTextReader(fs)) + { + variantsJson = JObject.Load(jr); + } + } + EnumDefinition[] enums = typesJson["enums"].Select(jt => { JProperty jp = (JProperty)jt; @@ -195,11 +205,20 @@ namespace CodeGenerator List parameters = new List(); + // find any variants that can be applied to the parameters of this method based on the method name + JToken variants = variantsJson?.Children().Where(variant => ((JProperty)variant).Name == cimguiname).FirstOrDefault() ?? null; + foreach (JToken p in val["argsT"]) { string pType = p["type"].ToString(); string pName = p["name"].ToString(); - parameters.Add(new TypeReference(pName, pType, enums)); + + // if there are possible variants for this method then try to match them based on the parameter name and expected type + var matchingVariant = variants?.Values().Where(v => v["name"].ToString() == pName && v["type"].ToString() == pType).FirstOrDefault() ?? null; + // if there was a match to this name and type then apply those variants to this parameter + string[] pVariants = matchingVariant?["variants"].Values().Select(variant => variant.ToString()).ToArray() ?? null; + + parameters.Add(new TypeReference(pName, pType, enums, pVariants)); } Dictionary defaultValues = new Dictionary(); @@ -231,7 +250,7 @@ namespace CodeGenerator isDestructor); }).Where(od => od != null).ToArray(); - return new FunctionDefinition(name, overloads); + return new FunctionDefinition(name, overloads, enums); }).OrderBy(fd => fd.Name).ToArray(); foreach (EnumDefinition ed in enums) @@ -1049,11 +1068,18 @@ namespace CodeGenerator public string TemplateType { get; } public int ArraySize { get; } public bool IsFunctionPointer { get; } + public string[] TypeVariants { get; } public TypeReference(string name, string type, EnumDefinition[] enums) - : this(name, type, null, enums) { } + : this(name, type, null, enums, null) { } + + public TypeReference(string name, string type, EnumDefinition[] enums, string[] typeVariants) + : this(name, type, null, enums, typeVariants) { } public TypeReference(string name, string type, string templateType, EnumDefinition[] enums) + : this(name, type, templateType, enums, null) { } + + public TypeReference(string name, string type, string templateType, EnumDefinition[] enums, string[] typeVariants) { Name = name; Type = type.Replace("const", string.Empty).Trim(); @@ -1082,6 +1108,8 @@ namespace CodeGenerator } IsFunctionPointer = Type.IndexOf('(') != -1; + + TypeVariants = typeVariants; } private int ParseSizeString(string sizePart, EnumDefinition[] enums) @@ -1117,6 +1145,12 @@ namespace CodeGenerator return ret; } + + public TypeReference WithVariant(int variantIndex, EnumDefinition[] enums) + { + if (variantIndex == 0) return this; + else return new TypeReference(Name, TypeVariants[variantIndex - 1], TemplateType, enums); + } } class FunctionDefinition @@ -1124,10 +1158,63 @@ namespace CodeGenerator public string Name { get; } public OverloadDefinition[] Overloads { get; } - public FunctionDefinition(string name, OverloadDefinition[] overloads) + public FunctionDefinition(string name, OverloadDefinition[] overloads, EnumDefinition[] enums) { Name = name; - Overloads = overloads; + Overloads = ExpandOverloadVariants(overloads, enums); + } + + private OverloadDefinition[] ExpandOverloadVariants(OverloadDefinition[] overloads, EnumDefinition[] enums) + { + List newDefinitions = new List(); + + foreach (OverloadDefinition overload in overloads) + { + bool hasVariants = false; + int[] variantCounts = new int[overload.Parameters.Length]; + + for (int i = 0; i < overload.Parameters.Length; i++) + { + if (overload.Parameters[i].TypeVariants != null) + { + hasVariants = true; + variantCounts[i] = overload.Parameters[i].TypeVariants.Length + 1; + } + else + { + variantCounts[i] = 1; + } + } + + if (hasVariants) + { + int totalVariants = variantCounts[0]; + for (int i = 1; i < variantCounts.Length; i++) totalVariants *= variantCounts[i]; + + for (int i = 0; i < totalVariants; i++) + { + TypeReference[] parameters = new TypeReference[overload.Parameters.Length]; + int div = 1; + + for (int j = 0; j < parameters.Length; j++) + { + int k = (i / div) % variantCounts[j]; + + parameters[j] = overload.Parameters[j].WithVariant(k, enums); + + if (j > 0) div *= variantCounts[j]; + } + + newDefinitions.Add(overload.WithParameters(parameters)); + } + } + else + { + newDefinitions.Add(overload); + } + } + + return newDefinitions.ToArray(); } } @@ -1166,6 +1253,11 @@ namespace CodeGenerator IsConstructor = isConstructor; IsDestructor = isDestructor; } + + public OverloadDefinition WithParameters(TypeReference[] parameters) + { + return new OverloadDefinition(ExportedName, FriendlyName, parameters, DefaultValues, ReturnType, StructName, Comment, IsConstructor, IsDestructor); + } } class MarshalledParameter From 5afed427add26ffa681f54dcbe4540e7b0bc826f Mon Sep 17 00:00:00 2001 From: giawa Date: Tue, 18 Jun 2019 14:02:20 -0700 Subject: [PATCH 2/4] Add example variants.json that adds IntPtr* as a parameter variant for ImFontAtlas_GetTexDataAsAlpha8 and ImFontAtlas_GetTexDataAsRGBA32. Rerun code generation. --- src/CodeGenerator/CodeGenerator.csproj | 7 +++ src/CodeGenerator/variants.json | 16 ++++++ src/ImGui.NET/Generated/ImFontAtlas.gen.cs | 60 ++++++++++++++++++++++ src/ImGui.NET/Generated/ImGuiNative.gen.cs | 4 ++ 4 files changed, 87 insertions(+) create mode 100644 src/CodeGenerator/variants.json diff --git a/src/CodeGenerator/CodeGenerator.csproj b/src/CodeGenerator/CodeGenerator.csproj index 9f05ccc..861e6a6 100644 --- a/src/CodeGenerator/CodeGenerator.csproj +++ b/src/CodeGenerator/CodeGenerator.csproj @@ -5,9 +5,16 @@ netcoreapp2.1 + + + + + + PreserveNewest + diff --git a/src/CodeGenerator/variants.json b/src/CodeGenerator/variants.json new file mode 100644 index 0000000..8b94900 --- /dev/null +++ b/src/CodeGenerator/variants.json @@ -0,0 +1,16 @@ +{ + "ImFontAtlas_GetTexDataAsAlpha8": [ + { + "name": "out_pixels", + "type": "unsigned char**", + "variants": [ "IntPtr*" ] + } + ], + "ImFontAtlas_GetTexDataAsRGBA32": [ + { + "name": "out_pixels", + "type": "unsigned char**", + "variants": [ "IntPtr*" ] + } + ] +} \ No newline at end of file diff --git a/src/ImGui.NET/Generated/ImFontAtlas.gen.cs b/src/ImGui.NET/Generated/ImFontAtlas.gen.cs index d398228..70a2c5d 100644 --- a/src/ImGui.NET/Generated/ImFontAtlas.gen.cs +++ b/src/ImGui.NET/Generated/ImFontAtlas.gen.cs @@ -434,6 +434,36 @@ namespace ImGuiNET } } } + public void GetTexDataAsAlpha8(out IntPtr out_pixels, out int out_width, out int out_height) + { + int* out_bytes_per_pixel = null; + fixed (IntPtr* native_out_pixels = &out_pixels) + { + fixed (int* native_out_width = &out_width) + { + fixed (int* native_out_height = &out_height) + { + ImGuiNative.ImFontAtlas_GetTexDataAsAlpha8(NativePtr, native_out_pixels, native_out_width, native_out_height, out_bytes_per_pixel); + } + } + } + } + public void GetTexDataAsAlpha8(out IntPtr out_pixels, out int out_width, out int out_height, out int out_bytes_per_pixel) + { + fixed (IntPtr* native_out_pixels = &out_pixels) + { + fixed (int* native_out_width = &out_width) + { + fixed (int* native_out_height = &out_height) + { + fixed (int* native_out_bytes_per_pixel = &out_bytes_per_pixel) + { + ImGuiNative.ImFontAtlas_GetTexDataAsAlpha8(NativePtr, native_out_pixels, native_out_width, native_out_height, native_out_bytes_per_pixel); + } + } + } + } + } public void GetTexDataAsRGBA32(out byte* out_pixels, out int out_width, out int out_height) { int* out_bytes_per_pixel = null; @@ -464,6 +494,36 @@ namespace ImGuiNET } } } + public void GetTexDataAsRGBA32(out IntPtr out_pixels, out int out_width, out int out_height) + { + int* out_bytes_per_pixel = null; + fixed (IntPtr* native_out_pixels = &out_pixels) + { + fixed (int* native_out_width = &out_width) + { + fixed (int* native_out_height = &out_height) + { + ImGuiNative.ImFontAtlas_GetTexDataAsRGBA32(NativePtr, native_out_pixels, native_out_width, native_out_height, out_bytes_per_pixel); + } + } + } + } + public void GetTexDataAsRGBA32(out IntPtr out_pixels, out int out_width, out int out_height, out int out_bytes_per_pixel) + { + fixed (IntPtr* native_out_pixels = &out_pixels) + { + fixed (int* native_out_width = &out_width) + { + fixed (int* native_out_height = &out_height) + { + fixed (int* native_out_bytes_per_pixel = &out_bytes_per_pixel) + { + ImGuiNative.ImFontAtlas_GetTexDataAsRGBA32(NativePtr, native_out_pixels, native_out_width, native_out_height, native_out_bytes_per_pixel); + } + } + } + } + } public bool IsBuilt() { byte ret = ImGuiNative.ImFontAtlas_IsBuilt(NativePtr); diff --git a/src/ImGui.NET/Generated/ImGuiNative.gen.cs b/src/ImGui.NET/Generated/ImGuiNative.gen.cs index 2ee31ac..abc6bf0 100644 --- a/src/ImGui.NET/Generated/ImGuiNative.gen.cs +++ b/src/ImGui.NET/Generated/ImGuiNative.gen.cs @@ -895,8 +895,12 @@ namespace ImGuiNET [DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)] public static extern void ImFontAtlas_GetTexDataAsAlpha8(ImFontAtlas* self, byte** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel); [DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)] + public static extern void ImFontAtlas_GetTexDataAsAlpha8(ImFontAtlas* self, IntPtr* out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel); + [DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)] public static extern void ImFontAtlas_GetTexDataAsRGBA32(ImFontAtlas* self, byte** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel); [DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)] + public static extern void ImFontAtlas_GetTexDataAsRGBA32(ImFontAtlas* self, IntPtr* out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel); + [DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)] public static extern ImFontAtlas* ImFontAtlas_ImFontAtlas(); [DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)] public static extern byte ImFontAtlas_IsBuilt(ImFontAtlas* self); From 8be7644c3e50db87e1a7a6be9602eb12d438e969 Mon Sep 17 00:00:00 2001 From: giawa Date: Tue, 18 Jun 2019 14:03:25 -0700 Subject: [PATCH 3/4] Modify SampleProgram to use 'safe' version of GetTexDataAsRGBA32, which was auto-generated by the CodeGenerator with the new variant functionality. --- src/ImGui.NET.SampleProgram/ImGuiController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImGui.NET.SampleProgram/ImGuiController.cs b/src/ImGui.NET.SampleProgram/ImGuiController.cs index 64d6ef9..ca40da7 100644 --- a/src/ImGui.NET.SampleProgram/ImGuiController.cs +++ b/src/ImGui.NET.SampleProgram/ImGuiController.cs @@ -244,11 +244,11 @@ namespace ImGuiNET /// /// Recreates the device texture used to render text. /// - public unsafe void RecreateFontDeviceTexture(GraphicsDevice gd) + public void RecreateFontDeviceTexture(GraphicsDevice gd) { ImGuiIOPtr io = ImGui.GetIO(); // Build - byte* pixels; + IntPtr pixels; int width, height, bytesPerPixel; io.Fonts.GetTexDataAsRGBA32(out pixels, out width, out height, out bytesPerPixel); // Store our identifier @@ -264,7 +264,7 @@ namespace ImGuiNET _fontTexture.Name = "ImGui.NET Font Texture"; gd.UpdateTexture( _fontTexture, - (IntPtr)pixels, + pixels, (uint)(bytesPerPixel * width * height), 0, 0, From d62cb5f27822e55ae12badbc68a8f26bbaa59ddd Mon Sep 17 00:00:00 2001 From: giawa Date: Tue, 18 Jun 2019 14:39:39 -0700 Subject: [PATCH 4/4] Process the variants into a dictionary and then mark them as used as they are applied. Loop over all variants at the end and display an error if the variant was unused. --- src/CodeGenerator/Program.cs | 61 +++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/src/CodeGenerator/Program.cs b/src/CodeGenerator/Program.cs index f3e1441..5c87592 100644 --- a/src/CodeGenerator/Program.cs +++ b/src/CodeGenerator/Program.cs @@ -140,6 +140,17 @@ namespace CodeGenerator } } + Dictionary variants = new Dictionary(); + foreach (var jt in variantsJson.Children()) + { + JProperty jp = (JProperty)jt; + ParameterVariant[] methodVariants = jp.Values().Select(jv => + { + return new ParameterVariant(jv["name"].ToString(), jv["type"].ToString(), jv["variants"].Select(s => s.ToString()).ToArray()); + }).ToArray(); + variants.Add(jp.Name, new MethodVariant(jp.Name, methodVariants)); + } + EnumDefinition[] enums = typesJson["enums"].Select(jt => { JProperty jp = (JProperty)jt; @@ -206,7 +217,8 @@ namespace CodeGenerator List parameters = new List(); // find any variants that can be applied to the parameters of this method based on the method name - JToken variants = variantsJson?.Children().Where(variant => ((JProperty)variant).Name == cimguiname).FirstOrDefault() ?? null; + MethodVariant methodVariants = null; + variants.TryGetValue(jp.Name, out methodVariants); foreach (JToken p in val["argsT"]) { @@ -214,11 +226,10 @@ namespace CodeGenerator string pName = p["name"].ToString(); // if there are possible variants for this method then try to match them based on the parameter name and expected type - var matchingVariant = variants?.Values().Where(v => v["name"].ToString() == pName && v["type"].ToString() == pType).FirstOrDefault() ?? null; - // if there was a match to this name and type then apply those variants to this parameter - string[] pVariants = matchingVariant?["variants"].Values().Select(variant => variant.ToString()).ToArray() ?? null; + ParameterVariant matchingVariant = methodVariants?.Parameters.Where(pv => pv.Name == pName && pv.OriginalType == pType).FirstOrDefault() ?? null; + if (matchingVariant != null) matchingVariant.Used = true; - parameters.Add(new TypeReference(pName, pType, enums, pVariants)); + parameters.Add(new TypeReference(pName, pType, enums, matchingVariant?.VariantTypes)); } Dictionary defaultValues = new Dictionary(); @@ -564,6 +575,14 @@ namespace CodeGenerator writer.PopBlock(); writer.PopBlock(); } + + foreach (var method in variants) + { + foreach (var variant in method.Value.Parameters) + { + if (!variant.Used) Console.WriteLine($"Error: Variants targetting parameter {variant.Name} with type {variant.OriginalType} could not be applied to method {method.Key}."); + } + } } private static bool IsStringFieldName(string name) @@ -982,6 +1001,38 @@ namespace CodeGenerator } } + class MethodVariant + { + public string Name { get; } + + public ParameterVariant[] Parameters { get; } + + public MethodVariant(string name, ParameterVariant[] parameters) + { + Name = name; + Parameters = parameters; + } + } + + class ParameterVariant + { + public string Name { get; } + + public string OriginalType { get; } + + public string[] VariantTypes { get; } + + public bool Used { get; set; } + + public ParameterVariant(string name, string originalType, string[] variantTypes) + { + Name = name; + OriginalType = originalType; + VariantTypes = variantTypes; + Used = false; + } + } + class EnumDefinition { private readonly Dictionary _sanitizedNames;