zoukankan      html  css  js  c++  java
  • Vulkan(1)用apispec生成Vulkan库

    Vulkan(1)用apispec生成Vulkan库

    我的Vulkan.net库已在(https://github.com/bitzhuwei/Vulkan.net)开源,欢迎交流。

    apispec.html

    在Vulkan SDK的安装文件夹里,有一个Documentationapispec.html文件。这是一个由代码生成的对Vulkan API的说明。它包含了Vulkan API的枚举类型、结构体、函数声明以及这一切的详细注释

    由于它是自动生成的,所以其格式非常规则。只需将少数几处<br>改为<br />,几处<col .. >改为<col .. />,就可以直接用 XElement 来加载和解析它。

    由于它包含了每个枚举类型及其成员的注释,包含了每个结构体及其成员的注释,包含了每个函数声明及其参数的注释,我就想,如果我能将它转换为C#代码,那会是多么美妙的一个Vulkan库啊!

    我在网上找到的几个Vulkan库,基本上都没有什么注释,这让我使用起来很不方便,严重妨碍了学习速度。很多结构体的成员类型都是粗糙的 IntPtr ,而不是具体类型的指针,这也使得用起来很麻烦。

    那么就动手做自己的Vulkan库吧!

    分类

    首先,要将巨大的apispec.html文件里的内容分为几个类别,即C宏定义、Command(函数声明)、Enum、Extension、Flag、Handle、PFN、Scalar Type和Struct。其中的C宏定义和Extension暂时用不到,就不管了,Scalar Type数量很少,又不包含实质内容,直接手工编写即可。

    我们按照Enum、Handle、Flag、PFN、Struct和Command的顺序依次分析,因为后者可能依赖前者。

    Enum

    我们来观察apispec.html中对Enum的描述:

    <h4 id="_name_798">Name</h4>
    <div class="paragraph">
    <p>VkAccelerationStructureMemoryRequirementsTypeNV - Acceleration structure memory requirement type</p>
    </div>
    </div>
    <div class="sect3">
    <h4 id="_c_specification_798">C Specification</h4>
    <div class="paragraph">
    <p>Possible values of <code>type</code> in
    <code>VkAccelerationStructureMemoryRequirementsInfoNV</code> are:,</p>
    </div>
    <div id="VkAccelerationStructureMemoryRequirementsTypeNV" class="listingblock">
    <div class="content">
    <pre class="highlight"><code class="language-c++" data-lang="c++">typedef enum VkAccelerationStructureMemoryRequirementsTypeNV {
        VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV = 0,
        VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV = 1,
        VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV = 2,
        VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_MAX_ENUM_NV = 0x7FFFFFFF
    } VkAccelerationStructureMemoryRequirementsTypeNV;</code></pre>
    </div>
    </div>
    </div>
    <div class="sect3">
    <h4 id="_description_798">Description</h4>
    <div class="ulist">
    <ul>
    <li>
    <p><code>VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV</code>
    requests the memory requirement for the <code>VkAccelerationStructureNV</code>
    backing store.</p>
    </li>
    <li>
    <p><code>VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV</code>
    requests the memory requirement for scratch space during the initial
    build.</p>
    </li>
    <li>
    <p><code>VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV</code>
    requests the memory requirement for scratch space during an update.</p>
    </li>
    </ul>
    </div>
    </div>
    <div class="sect3">
    <h4 id="_see_also_798">See Also</h4>

    我们将发现,对于每个Enum类型,apispec都有这样的规律:从一个<h4>Name</h4>标签开始,接下来的<p></p>标签是对这个Enum的注释,接下来的<code class="language-c++"></code>标签是这个Enum的定义;然后,从<h4>Descriptor</h4>开始到<h4>See Also</h4>结束,这两个标签之间的所有<p></p>标签,分别是Enum的某个成员的注释,而且,这个注释都是以<code>此成员的名字</code>开头(这可以用于识别此注释属于哪个成员)。

    有了这些规律,就可以将其解析为C#代码了。解析代码很简单,就不解释了。

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Xml.Linq;
      4 
      5 namespace ApiSpec {
      6     class EnumsParser {
      7 
      8         static readonly char[] inLineSeparator = new char[] { ' ', '	', '
    ', '
    ', };
      9         static readonly char[] lineSeparator = new char[] { '
    ', '
    ' };
     10         const string leftBrace = "{";
     11         const string rightBrace = "}";
     12 
     13         const string filename = "Enums.content.xml";
     14         const string strName = "Name";
     15         const string strCSpecification = "C Specification";
     16         const string strDescription = "Description";
     17         const string strSeeAlso = "See Also";
     18         const string strDocNotes = "Document Notes";
     19 
     20         class EnumDefinetion {
     21             /*typedef enum VkAccelerationStructureMemoryRequirementsTypeNV {
     22     VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV = 0,
     23     VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV = 1,
     24     VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV = 2,
     25     VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_MAX_ENUM_NV = 0x7FFFFFFF
     26 } VkAccelerationStructureMemoryRequirementsTypeNV;
     27              */
     28             public string raw;
     29 
     30             public string[] Dump() {
     31                 string[] lines = this.raw.Split(lineSeparator, StringSplitOptions.RemoveEmptyEntries);
     32                 if (lines == null || lines.Length < 2) { return lines; }
     33 
     34                 {
     35                     string[] parts = lines[0].Split(inLineSeparator, StringSplitOptions.RemoveEmptyEntries);
     36                     lines[0] = $"public enum {parts[2]} {leftBrace}";
     37                 }
     38                 {
     39                     int last = lines.Length - 1;
     40                     lines[last] = $"{rightBrace}";
     41                 }
     42 
     43                 return lines;
     44             }
     45         }
     46 
     47         class EnumItemComment {
     48             public List<string> lstComment = new List<string>();
     49 
     50             public Dictionary<string, string> Dump() {
     51                 Dictionary<string, string> dict = new Dictionary<string, string>();
     52                 foreach (var item in lstComment) {
     53                     int left = item.IndexOf("<code>");
     54                     int right = item.IndexOf("</code>");
     55                     if (left != -1 && right != -1) {
     56                         string key = item.Substring(left + "<code>".Length, right - (left + "<code>".Length));
     57                         if (!dict.ContainsKey(key)) {
     58                             dict.Add(key, item);
     59                         }
     60                     }
     61                 }
     62 
     63                 return dict;
     64             }
     65         }
     66 
     67         public static void DumpEnums() {
     68             XElement root = XElement.Load(filename);
     69             var lstDefinition = new List<EnumDefinetion>(); bool inside = false;
     70             TraverseNodesEnumDefinitions(root, lstDefinition, ref inside);
     71             var listEnumItemComment = new List<EnumItemComment>(); inside = false;
     72             TraverseNodesEnumItemComments(root, listEnumItemComment, ref inside);
     73             var lstEnumComment = new List<string>(); inside = false;
     74             TraverseNodesEnumComments(root, lstEnumComment, ref inside);
     75 
     76             using (var sw = new System.IO.StreamWriter("Enums.gen.cs")) {
     77                 for (int i = 0; i < lstDefinition.Count; i++) {
     78                     EnumDefinetion definition = lstDefinition[i];
     79                     //sw.WriteLine(definition.raw);
     80                     string[] definitionLines = definition.Dump();
     81                     EnumItemComment itemComment = listEnumItemComment[i];
     82                     Dictionary<string, string> item2Comment = itemComment.Dump();
     83 
     84                     sw.WriteLine($"// Enum: {i}");
     85                     string enumComment = lstEnumComment[i];
     86                     sw.WriteLine($"/// <summary>{enumComment}</summary>");
     87                     {
     88                         string line = definitionLines[0];
     89                         if (line.Contains("FlagBits")) { sw.WriteLine("[Flags]"); }
     90                         sw.WriteLine(line);
     91                     }
     92                     for (int j = 1; j < definitionLines.Length - 1; j++) {
     93                         string line = definitionLines[j];
     94                         if (item2Comment != null) {
     95                             string strComment = ParseItemComment(line, item2Comment);
     96                             if (strComment != string.Empty) {
     97                                 strComment = strComment.Replace("
    ", "
    ");
     98                                 strComment = strComment.Replace("
    ", "
    ");
     99                                 strComment = strComment.Replace("
    ", $"{Environment.NewLine}    /// ");
    100                                 sw.WriteLine($"    /// <summary>{strComment}</summary>");
    101                             }
    102                         }
    103                         sw.WriteLine(line);
    104                     }
    105                     {
    106                         string line = definitionLines[definitionLines.Length - 1];
    107                         sw.WriteLine(line); // }
    108                     }
    109                 }
    110             }
    111             Console.WriteLine("Done");
    112         }
    113 
    114         /*<h4 id="_name_800">Name</h4>
    115 <div class="paragraph">
    116 <p>VkAccessFlagBits - Bitmask specifying memory access types that will participate in a memory dependency</p>
    117 </div>*/
    118         private static void TraverseNodesEnumComments(XElement node, List<string> list, ref bool inside) {
    119             if (node.Name == "h4") {
    120                 if (node.Value == "Name") {
    121                     inside = true;
    122                 }
    123             }
    124             else if (node.Name == "p") {
    125                 if (inside) {
    126                     string text = node.ToString();
    127                     text = text.Substring("<p>".Length, text.Length - "<p></p>".Length);
    128                     text = text.Trim();
    129                     list.Add(text);
    130                     inside = false;
    131                 }
    132             }
    133 
    134             foreach (XElement item in node.Elements()) {
    135                 TraverseNodesEnumComments(item, list, ref inside);
    136             }
    137         }
    138 
    139         /* line:    VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV = 0,
    140          *     
    141         comment: <code>VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV</code> is a top-level
    142         acceleration structure containing instance data referring to
    143 bottom-level level acceleration structures.
    144 <code>VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV</code> is a bottom-level
    145 acceleration structure containing the AABBs or geometry to be
    146 intersected.
    147     */
    148         static readonly char[] equalSeparator = new char[] { '=', ' ', '	', '
    ', '
    ', };
    149         private static string ParseItemComment(string line, Dictionary<string, string> dict) {
    150             string result = string.Empty;
    151             string[] parts = line.Split(equalSeparator, StringSplitOptions.RemoveEmptyEntries);
    152             if (parts.Length == 2) {
    153                 string key = parts[0];
    154                 if (dict.ContainsKey(key)) {
    155                     result = dict[key];
    156                 }
    157             }
    158 
    159             return result;
    160         }
    161 
    162         /// <summary>
    163         /// 
    164         /// </summary>
    165         /// <param name="node"></param>
    166         /// <param name="list"></param>
    167         /// <param name="inside"></param>
    168         private static void TraverseNodesEnumItemComments(XElement node, List<EnumItemComment> list, ref bool inside) {
    169             if (node.Name == "h4") {
    170                 if (node.Value == "Description") {
    171                     inside = true;
    172                     var comment = new EnumItemComment();
    173                     list.Add(comment);
    174                 }
    175                 else if (node.Value == "See Also") {
    176                     inside = false;
    177                 }
    178             }
    179             else if (node.Name == "p") {
    180                 if (inside) {
    181                     EnumItemComment comment = list[list.Count - 1];
    182                     string text = node.ToString();
    183                     text = text.Substring("<p>".Length, text.Length - "<p></p>".Length);
    184                     text = text.Trim();
    185                     comment.lstComment.Add(text);
    186                 }
    187             }
    188 
    189             foreach (XElement item in node.Elements()) {
    190                 TraverseNodesEnumItemComments(item, list, ref inside);
    191             }
    192         }
    193 
    194 
    195         private static void TraverseNodesEnumDefinitions(XElement node, List<EnumDefinetion> list, ref bool inside) {
    196             if (node.Name == "h4") {
    197                 if (node.Value == "C Specification") {
    198                     inside = true;
    199                 }
    200             }
    201             else if (node.Name == "code") {
    202                 if (inside) {
    203                     XAttribute attrClass = node.Attribute("class");
    204                     if (attrClass != null && attrClass.Value == "language-c++") {
    205                         string v = node.Value;
    206                         var item = new EnumDefinetion() { raw = v, };
    207                         list.Add(item);
    208                         inside = false;
    209                     }
    210                 }
    211             }
    212 
    213             foreach (XElement item in node.Elements()) {
    214                 TraverseNodesEnumDefinitions(item, list, ref inside);
    215             }
    216         }
    217     }
    218 }
    EnumsParser

    解析得到了143个Enum类型,其中前2个如下:

     1     // Enum: 0
     2     /// <summary>VkAccelerationStructureMemoryRequirementsTypeNV - Acceleration structure memory requirement type</summary>
     3     public enum VkAccelerationStructureMemoryRequirementsTypeNV {
     4         /// <summary><code>VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV</code>
     5         /// requests the memory requirement for the <code>VkAccelerationStructureNV</code>
     6         /// backing store.</summary>
     7         VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV = 0,
     8         /// <summary><code>VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV</code>
     9         /// requests the memory requirement for scratch space during the initial
    10         /// build.</summary>
    11         VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV = 1,
    12         /// <summary><code>VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV</code>
    13         /// requests the memory requirement for scratch space during an update.</summary>
    14         VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV = 2,
    15         VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_MAX_ENUM_NV = 0x7FFFFFFF
    16     }
    17     // Enum: 1
    18     /// <summary>VkAccelerationStructureTypeNV - Type of acceleration structure</summary>
    19     public enum VkAccelerationStructureTypeNV {
    20         /// <summary><code>VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV</code> is a top-level
    21         /// acceleration structure containing instance data referring to
    22         /// bottom-level level acceleration structures.</summary>
    23         VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV = 0,
    24         /// <summary><code>VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV</code> is a bottom-level
    25         /// acceleration structure containing the AABBs or geometry to be
    26         /// intersected.</summary>
    27         VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV = 1,
    28         VK_ACCELERATION_STRUCTURE_TYPE_MAX_ENUM_NV = 0x7FFFFFFF
    29     }

    为了保持Vulkan API的原汁原味(也为了我自己省事),Enum的成员名字就保持这么长的大写+下划线版本好了。

    Handle

    这里的Handle指的是Vulkan中的不透明对象提供给程序员的句柄,例如一个VkInstance类型的对象,在程序员这里看到的只是一个UInt32的句柄,它的实际内容由Vulkan内部来管理。因此这里只需找到各个Handle的名字,将其改写为一个struct即可。

    在apispec.html中对Handle的描述如下:

    <h3 id="_vkaccelerationstructurenv3">VkAccelerationStructureNV(3)</h3>

    只需找到各个<h3></h3>标签,就可以找到各个Handle的名字了。解析后得到37个Handle,其中的2个Handle如下:

     1     // Object Handles: 1
     2     /// <summary>VkBuffer - Opaque handle to a buffer object
     3     /// <para>Buffers represent linear arrays of data which are used for various purposesby binding them to a graphics or compute pipeline via descriptor sets or viacertain commands, or by directly specifying them as parameters to certaincommands.</para>
     4     /// <para>Buffers are represented by VkBuffer handles:</para>
     5     /// </summary>
     6     public struct VkBuffer {
     7         public UInt64 handle;
     8     }
     9 
    10     // Object Handles: 21
    11     /// <summary>VkInstance - Opaque handle to an instance object
    12     /// <para>There is no global state in Vulkan and all per-application state is storedin a VkInstance object.Creating a VkInstance object initializes the Vulkan library and allowsthe application to pass information about itself to the implementation.</para>
    13     /// <para>Instances are represented by VkInstance handles:</para>
    14     /// </summary>
    15     public struct VkInstance {
    16         public UInt32 handle;
    17     }

    对于上述这样的struct,其长度等于内部成员的长度。因此,实际上VkInstance只是UInt32的一个别名,这样的别名大大强化了类型的作用,加快了编程速度。

    要注意的是,有的Handle使用UInt64,有的使用UInt32,这是不可以随意改变的,否则Vulkan会卡住不动。当然,只要字节长度相同,就可以代替,例如可以用IntPtr代替UInt32,因为两者都是4字节的。

    Flag

    在apispec.html中,Flag实际上是一个别名,即C语言中用 typedef 定义的一个名字。2个例子如下:

    1 <p>VkAccessFlags - Bitmask of VkAccessFlagBits</p>
    2 <p>VkBufferViewCreateFlags - Reserved for future use</p>

    这是目前的apispec中仅有的2种Flag的说明形式。对于它们,我们分别可以用下面的代码代替:

    1 using VkAccessFlags = ApiSpec.Generated.VkAccessFlagBits;
    2 // VkBufferViewCreateFlags - Reserved for future use

    解析方法也很简单,用 string.Split() 拆分一下即可。

    最后得到的这些using代码,将用于后面解析的Struct和Command中。

    PFN

    这里的PFN是函数指针的意思,也就是C#里的delegate那一套。其解析方式与Enum十分相似,不再赘述。解析后得到了8个函数指针的定义,其中几个如下:

     1     // PFN: 0
     2     /// <summary>PFN_vkAllocationFunction - Application-defined memory allocation function</summary>
     3     public unsafe delegate void* PFN_vkAllocationFunction(
     4     /// <summary>pUserData is the value specified for
     5     /// VkAllocationCallbacks::pUserData in the allocator specified
     6     /// by the application.</summary>
     7     void* pUserData,
     8     /// <summary>size is the size in bytes of the requested allocation.</summary>
     9     Int32 size,
    10     /// <summary>alignment is the requested alignment of the allocation in bytes
    11     /// and must be a power of two.</summary>
    12     Int32 alignment,
    13     /// <summary>allocationScope is a VkSystemAllocationScope value
    14     /// specifying the allocation scope of the lifetime of the allocation, as
    15     /// described here.</summary>
    16     VkSystemAllocationScope allocationScope);
    17     // PFN: 1
    18     /// <summary>PFN_vkDebugReportCallbackEXT - Application-defined debug report callback function</summary>
    19     public unsafe delegate VkBool32 PFN_vkDebugReportCallbackEXT(
    20     /// <summary>flags specifies the VkDebugReportFlagBitsEXT that triggered
    21     /// this callback.</summary>
    22     VkDebugReportFlagBitsEXT flags,
    23     /// <summary>objectType is a VkDebugReportObjectTypeEXT value specifying
    24     /// the type of object being used or created at the time the event was
    25     /// triggered.</summary>
    26     VkDebugReportObjectTypeEXT _objectType,
    27     /// <summary>object is the object where the issue was detected.
    28     /// If objectType is VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
    29     /// object is undefined.</summary>
    30     UInt64 _object,
    31     /// <summary>location is a component (layer, driver, loader) defined value that
    32     /// specifies the location of the trigger.
    33     /// This is an optional value.</summary>
    34     Int32 location,
    35     /// <summary>messageCode is a layer-defined value indicating what test
    36     /// triggered this callback.</summary>
    37     Int32 messageCode,
    38     /// <summary>pLayerPrefix is a null-terminated string that is an abbreviation
    39     /// of the name of the component making the callback.
    40     /// pLayerPrefix is only valid for the duration of the callback.</summary>
    41     IntPtr pLayerPrefix,
    42     /// <summary>pMessage is a null-terminated string detailing the trigger
    43     /// conditions.
    44     /// pMessage is only valid for the duration of the callback.</summary>
    45     IntPtr pMessage,
    46     /// <summary>pUserData is the user data given when the
    47     /// VkDebugReportCallbackEXT was created.</summary>
    48     void* pUserData);

    可以看到,函数注释和参数注释都十分详尽,看了就开心。

    Struct

    对于Struct的解析也与Enum类似,不再赘述。解析后得到434个结构体。其中几个如下:

     1     // Struct: 4
     2     /// <summary>VkAllocationCallbacks - Structure containing callback function pointers for memory allocation
     3     /// </summary>
     4     public unsafe struct VkAllocationCallbacks {
     5         /// <summary> pUserData is a value to be interpreted by the implementation of
     6         /// the callbacks.
     7         /// When any of the callbacks in VkAllocationCallbacks are called, the
     8         /// Vulkan implementation will pass this value as the first parameter to the
     9         /// callback.
    10         /// This value can vary each time an allocator is passed into a command,
    11         /// even when the same object takes an allocator in multiple commands.</summary>
    12         public void* pUserData;
    13         /// <summary> pfnAllocation is a pointer to an application-defined memory
    14         /// allocation function of type PFN_vkAllocationFunction.</summary>
    15         public /*PFN_vkAllocationFunction*/IntPtr pfnAllocation;
    16         /// <summary> pfnReallocation is a pointer to an application-defined memory
    17         /// reallocation function of type PFN_vkReallocationFunction.</summary>
    18         public /*PFN_vkReallocationFunction*/IntPtr pfnReallocation;
    19         /// <summary> pfnFree is a pointer to an application-defined memory free
    20         /// function of type PFN_vkFreeFunction.</summary>
    21         public /*PFN_vkFreeFunction*/IntPtr pfnFree;
    22         /// <summary> pfnInternalAllocation is a pointer to an application-defined
    23         /// function that is called by the implementation when the implementation
    24         /// makes internal allocations, and it is of type
    25         /// PFN_vkInternalAllocationNotification.</summary>
    26         public /*PFN_vkInternalAllocationNotification*/IntPtr pfnInternalAllocation;
    27         /// <summary> pfnInternalFree is a pointer to an application-defined function
    28         /// that is called by the implementation when the implementation frees
    29         /// internal allocations, and it is of type
    30         /// PFN_vkInternalFreeNotification.</summary>
    31         public /*PFN_vkInternalFreeNotification*/IntPtr pfnInternalFree;
    32 }
    33     // Struct: 9
    34     /// <summary>VkApplicationInfo - Structure specifying application info
    35     /// </summary>
    36     public unsafe struct VkApplicationInfo {
    37         /// <summary> sType is the type of this structure.</summary>
    38         public VkStructureType sType;
    39         /// <summary> pNext is NULL or a pointer to an extension-specific structure.</summary>
    40         public /*-const-*/ void* pNext;
    41         /// <summary> pApplicationName is NULL or is a pointer to a null-terminated
    42         /// UTF-8 string containing the name of the application.</summary>
    43         public IntPtr pApplicationName;
    44         /// <summary> applicationVersion is an unsigned integer variable containing the
    45         /// developer-supplied version number of the application.</summary>
    46         public UInt32 applicationVersion;
    47         /// <summary> pEngineName is NULL or is a pointer to a null-terminated UTF-8
    48         /// string containing the name of the engine (if any) used to create the
    49         /// application.</summary>
    50         public IntPtr pEngineName;
    51         /// <summary> engineVersion is an unsigned integer variable containing the
    52         /// developer-supplied version number of the engine used to create the
    53         /// application.</summary>
    54         public UInt32 engineVersion;
    55         /// <summary> apiVersion
    56         ///   must be the highest version of Vulkan that the
    57         /// application is designed to use, encoded as described in
    58         /// html/vkspec.html#extendingvulkan-coreversions-versionnumbers.
    59         /// The patch version number specified in apiVersion is ignored when
    60         /// creating an instance object.
    61         /// Only the major and minor versions of the instance must match those
    62         /// requested in apiVersion.</summary>
    63         public UInt32 apiVersion;
    64 }
    65     // Struct: 193
    66     /// <summary>VkInstanceCreateInfo - Structure specifying parameters of a newly created instance
    67     /// </summary>
    68     public unsafe struct VkInstanceCreateInfo {
    69         /// <summary> sType is the type of this structure.</summary>
    70         public VkStructureType sType;
    71         /// <summary> pNext is NULL or a pointer to an extension-specific structure.</summary>
    72         public /*-const-*/ void* pNext;
    73         /// <summary> flags is reserved for future use.</summary>
    74         public VkInstanceCreateFlags flags;
    75         /// <summary> pApplicationInfo is NULL or a pointer to an instance of
    76         /// VkApplicationInfo.
    77         /// If not NULL, this information helps implementations recognize behavior
    78         /// inherent to classes of applications.
    79         /// VkApplicationInfo is defined in detail below.</summary>
    80         public /*-const-*/ VkApplicationInfo* pApplicationInfo;
    81         /// <summary> enabledLayerCount is the number of global layers to enable.</summary>
    82         public UInt32 enabledLayerCount;
    83         /// <summary> ppEnabledLayerNames is a pointer to an array of
    84         /// enabledLayerCount null-terminated UTF-8 strings containing the
    85         /// names of layers to enable for the created instance.
    86         /// See the html/vkspec.html#extendingvulkan-layers section for further details.</summary>
    87         public IntPtr /*-const-*/ * ppEnabledLayerNames;
    88         /// <summary> enabledExtensionCount is the number of global extensions to
    89         /// enable.</summary>
    90         public UInt32 enabledExtensionCount;
    91         /// <summary> ppEnabledExtensionNames is a pointer to an array of
    92         /// enabledExtensionCount null-terminated UTF-8 strings containing the
    93         /// names of extensions to enable.</summary>
    94         public IntPtr /*-const-*/ * ppEnabledExtensionNames;
    95     }

    这里有几点要注意。

    函数委托用在struct中后,这个struct无法使用指针形式(SomeStruct*),所以这里不得不用IntPtr代替了具体的函数委托。

    在 IntPtr pApplicationName 中应当用 Marshal.StringToHGlobalAnsi(string s) 为其赋值。函数 Marshal.StringToHGlobalAnsi(string s) 会在非托管内存中为s创建一个副本,然后返回此副本的指针。这样pApplicationName才会指向一个固定位置的字符串。当然,用完后,这个副本应当用 Marshal.FreeHGlobal(IntPtr hglobal) 释放掉。为了简化这一过程,我提供一个扩展函数:

     1         /// <summary>
     2         /// Set a string to specified <paramref name="target"/>.
     3         /// </summary>
     4         /// <param name="value"></param>
     5         /// <param name="target">address of string.</param>
     6         public static void Set(this string value, ref IntPtr target) {
     7             {   // free unmanaged memory.
     8                 if (target != IntPtr.Zero) {
     9                     Marshal.FreeHGlobal(target);
    10                     target = IntPtr.Zero;
    11                 }
    12             }
    13             {
    14                 if (value != null && value.Length > 0) {
    15                     target = Marshal.StringToHGlobalAnsi(value);
    16                 }
    17                 else {
    18                     target = IntPtr.Zero;
    19                 }
    20             }
    21         }

    这个扩展函数会将上一次 Marshal.StringToHGlobalAnsi() 的内存释放,但是无法保证这次的。也就是说,它可以保证,最多还只需调用1次内存释放函数Marshal.FreeHGlobal(IntPtr hglobal)。

    在 public IntPtr /*-const-*/ * ppEnabledLayerNames; 中也有类似的问题,这个成员指向一个IntPtr数组,这个数组的每个成员都是一个IntPtr,每个IntPtr都指向一个由 Marshal.StringToHGlobalAnsi(string s) 提供的返回值。所以这需要另一个扩展函数来简化之:

     1         /// <summary>
     2         /// Set an array of structs to specified <paramref name="target"/> and <paramref name="count"/>.
     3         /// <para>Enumeration types are not allowed to use this method.
     4         /// If you have to, convert them to byte/short/ushort/int/uint according to their underlying types first.</para>
     5         /// </summary>
     6         /// <param name="value"></param>
     7         /// <param name="target">address of first element/array.</param>
     8         /// <param name="count">How many elements?</param>
     9         public static void Set<T>(this T[] value, ref IntPtr target, ref UInt32 count) where T : struct {
    10             {   // free unmanaged memory.
    11                 if (target != IntPtr.Zero) {
    12                     Marshal.FreeHGlobal(target);
    13                     target = IntPtr.Zero;
    14                     count = 0;
    15                 }
    16             }
    17             {
    18                 count = (UInt32)value.Length;
    19 
    20                 int elementSize = Marshal.SizeOf<T>();
    21                 int byteLength = (int)(count * elementSize);
    22                 IntPtr array = Marshal.AllocHGlobal(byteLength);
    23                 var dst = (byte*)array;
    24                 GCHandle pin = GCHandle.Alloc(value, GCHandleType.Pinned);
    25                 IntPtr address = Marshal.UnsafeAddrOfPinnedArrayElement(value, 0);
    26                 var src = (byte*)address;
    27                 for (int i = 0; i < byteLength; i++) {
    28                     dst[i] = src[i];
    29                 }
    30                 pin.Free();
    31 
    32                 target = array;
    33             }
    34         }

    在此函数参数中,我使用 ref IntPtr target ,而不是 ref T* target ,是因为C#不允许这样。编译器说,无法获取托管类型(”T”)的大小,或声明指向它的指针。那么在调用此扩展函数时,就得先创建一个临时变量 IntPtr ptr = IntPtr.Zero ,调用完扩展函数后,再将ptr赋予具体类型的指针。例如:

    1         var deviceInfo = new VkDeviceCreateInfo();
    2         IntPtr ptr = IntPtr.Zero;
    3         new VkDeviceQueueCreateInfo[] { queueInfo }.Set(ref ptr, ref deviceInfo.queueCreateInfoCount);
    4         deviceInfo.pQueueCreateInfos = (VkDeviceQueueCreateInfo*)ptr;

    好消息是,对于字符串数组string[]和(

    boolbyteshortintlongcharsbyteushortuintulongfloatdouble

    )这12种特殊基础类型的数组,可以直接使用Set扩展函数。因为我专门为它们编写了特定的扩展函数。

    Command

    对于Command的解析也与Struct类似,不再赘述。解析后得到326个command,几个例子如下:

     1         // Command: 4
     2         /// <summary>vkAllocateCommandBuffers - Allocate command buffers from an existing command pool
     3         /// </summary>
     4         /// <param name="device"> device is the logical device that owns the command pool.</param>
     5         /// <param name="pAllocateInfo"> pAllocateInfo is a pointer to an instance of the
     6         /// VkCommandBufferAllocateInfo structure describing parameters of the
     7         /// allocation.</param>
     8         /// <param name="pCommandBuffers"> pCommandBuffers is a pointer to an array of VkCommandBuffer
     9         /// handles in which the resulting command buffer objects are returned.
    10         /// The array must be at least the length specified by the
    11         /// commandBufferCount member of pAllocateInfo.
    12         /// Each allocated command buffer begins in the initial state.</param>
    13         [DllImport(VulkanLibrary, CallingConvention = CallingConvention.Winapi)]
    14         public static extern VkResult vkAllocateCommandBuffers(
    15             VkDevice device,
    16             /*-const-*/ VkCommandBufferAllocateInfo* pAllocateInfo,
    17             VkCommandBuffer* pCommandBuffers);
    18         // Command: 324
    19         /// <summary>vkUpdateDescriptorSets - Update the contents of a descriptor set object
    20         /// </summary>
    21         /// <param name="device"> device is the logical device that updates the descriptor sets.</param>
    22         /// <param name="descriptorWriteCount"> descriptorWriteCount is the number of elements in the
    23         /// pDescriptorWrites array.</param>
    24         /// <param name="pDescriptorWrites"> pDescriptorWrites is a pointer to an array of
    25         /// VkWriteDescriptorSet structures describing the descriptor sets to
    26         /// write to.</param>
    27         /// <param name="descriptorCopyCount"> descriptorCopyCount is the number of elements in the
    28         /// pDescriptorCopies array.</param>
    29         /// <param name="pDescriptorCopies"> pDescriptorCopies is a pointer to an array of
    30         /// VkCopyDescriptorSet structures describing the descriptor sets to
    31         /// copy between.</param>
    32         [DllImport(VulkanLibrary, CallingConvention = CallingConvention.Winapi)]
    33         public static extern void vkUpdateDescriptorSets(
    34             VkDevice device,
    35             UInt32 descriptorWriteCount,
    36             /*-const-*/ VkWriteDescriptorSet* pDescriptorWrites,
    37             UInt32 descriptorCopyCount,
    38             /*-const-*/ VkCopyDescriptorSet* pDescriptorCopies);

    其中有一个函数使用了 void** 这个二级指针,我觉得实在难看又难用,就用 IntPtr* 代替了。

    对非托管内存的管理(释放)问题

    每个struct都应该自己负责自己使用的非托管资源的释放问题。给这样的struct的指针成员 T* p; 赋值时,也应当为数据复制一个副本,将副本赋值给p。这样它释放资源时,就不会影响到其它地方了。实际上,在各个扩展函数 Set(..) 中,我就是用副本赋值的。

    如果struct的指针成员 T* p; 实际上只需得到1个对象,也就是说,数组中的元素只有1个,那么可以直接将此元素的地址赋值给p,并且释放资源。例如:

     1     UInt32 index = 0;
     2     var info = new VkSwapchainCreateInfoKHR();
     3     {
     4         info.sType = VkStructureType.VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
     5         // other stuff ..
     6         //new UInt32[] { 0 }.Set(ref info.QueueFamilyIndices, ref info.QueueFamilyIndexCount);
     7         info.pQueueFamilyIndices = &index; info.queueFamilyIndexCount = 1;
     8     }
     9     
    10     VkSwapchainKHR swapchain;
    11     vkAPI.vkCreateSwapchainKHR(device, &info, null, &swapchain);

    这是稳妥、可移植、无需程序员直接写 Marshal. AllocHGlobal() 的内存管理方法。

    那么,如果程序员忘记释放某些struct的资源了呢?Vulkan说,程序员应当清楚自己在做什么,不然他们何必用Vulkan。我觉得呢,这些struct不会被反复使用,因此,它们最多泄漏一点点内存,不会像服务器代码那样占用越来越多的内存,所以不碍事的。

    总结

    有了这么带劲的注释,整个档次都不一样了。

     

  • 相关阅读:
    4.iptables 网络防火墙
    iterator not dereferencable问题
    秒杀多线程系列中的题目
    说说僵尸和孤儿进程的那点儿事
    Linux的fork()写时复制原则(转)
    linux进程原语之fork()
    linux--多进程进行文件拷贝
    哈夫曼树与哈夫曼编码
    csp公共钥匙盒
    字典树
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/Vulkan-1-from-apispec.html
Copyright © 2011-2022 走看看