Vulkan(1)用apispec生成Vulkan库

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

apispec.html

在Vulkan SDK的安装文件夹里,有一个Documentation\apispec.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的描述:

  1. <h4 id="_name_798">Name</h4>
  2. <div class="paragraph">
  3. <p>VkAccelerationStructureMemoryRequirementsTypeNV - Acceleration structure memory requirement type</p>
  4. </div>
  5. </div>
  6. <div class="sect3">
  7. <h4 id="_c_specification_798">C Specification</h4>
  8. <div class="paragraph">
  9. <p>Possible values of <code>type</code> in
  10. <code>VkAccelerationStructureMemoryRequirementsInfoNV</code> are:,</p>
  11. </div>
  12. <div id="VkAccelerationStructureMemoryRequirementsTypeNV" class="listingblock">
  13. <div class="content">
  14. <pre class="highlight"><code class="language-c++" data-lang="c++">typedef enum VkAccelerationStructureMemoryRequirementsTypeNV {
  15. VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV = ,
  16. VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV = ,
  17. VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV = ,
  18. VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_MAX_ENUM_NV = 0x7FFFFFFF
  19. } VkAccelerationStructureMemoryRequirementsTypeNV;</code></pre>
  20. </div>
  21. </div>
  22. </div>
  23. <div class="sect3">
  24. <h4 id="_description_798">Description</h4>
  25. <div class="ulist">
  26. <ul>
  27. <li>
  28. <p><code>VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV</code>
  29. requests the memory requirement for the <code>VkAccelerationStructureNV</code>
  30. backing store.</p>
  31. </li>
  32. <li>
  33. <p><code>VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV</code>
  34. requests the memory requirement for scratch space during the initial
  35. build.</p>
  36. </li>
  37. <li>
  38. <p><code>VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV</code>
  39. requests the memory requirement for scratch space during an update.</p>
  40. </li>
  41. </ul>
  42. </div>
  43. </div>
  44. <div class="sect3">
  45. <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[] { ' ', '\t', '\r', '\n', };
  9. static readonly char[] lineSeparator = new char[] { '\r', '\n' };
  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 < ) { return lines; }
  33.  
  34. {
  35. string[] parts = lines[].Split(inLineSeparator, StringSplitOptions.RemoveEmptyEntries);
  36. lines[] = $"public enum {parts[2]} {leftBrace}";
  37. }
  38. {
  39. int last = lines.Length - ;
  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 != - && right != -) {
  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 = ; 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[];
  89. if (line.Contains("FlagBits")) { sw.WriteLine("[Flags]"); }
  90. sw.WriteLine(line);
  91. }
  92. for (int j = ; j < definitionLines.Length - ; 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("\r\n", "\n");
  98. strComment = strComment.Replace("\r", "\n");
  99. strComment = strComment.Replace("\n", $"{Environment.NewLine} /// ");
  100. sw.WriteLine($" /// <summary>{strComment}</summary>");
  101. }
  102. }
  103. sw.WriteLine(line);
  104. }
  105. {
  106. string line = definitionLines[definitionLines.Length - ];
  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[] { '=', ' ', '\t', '\r', '\n', };
  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 == ) {
  153. string key = parts[];
  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 - ];
  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. private static void TraverseNodesEnumDefinitions(XElement node, List<EnumDefinetion> list, ref bool inside) {
  195. if (node.Name == "h4") {
  196. if (node.Value == "C Specification") {
  197. inside = true;
  198. }
  199. }
  200. else if (node.Name == "code") {
  201. if (inside) {
  202. XAttribute attrClass = node.Attribute("class");
  203. if (attrClass != null && attrClass.Value == "language-c++") {
  204. string v = node.Value;
  205. var item = new EnumDefinetion() { raw = v, };
  206. list.Add(item);
  207. inside = false;
  208. }
  209. }
  210. }
  211.  
  212. foreach (XElement item in node.Elements()) {
  213. TraverseNodesEnumDefinitions(item, list, ref inside);
  214. }
  215. }
  216. }
  217. }

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 = ,
  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 = ,
  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 = ,
  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 = ,
  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 = ,
  28. VK_ACCELERATION_STRUCTURE_TYPE_MAX_ENUM_NV = 0x7FFFFFFF
  29. }

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

Handle

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

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

  1. <h3 id="_vkaccelerationstructurenv3">VkAccelerationStructureNV()</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 > ) {
  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 = ;
  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, );
  26. var src = (byte*)address;
  27. for (int i = ; 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[]和(

  1. 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 = ;
  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 = ;
  8. }
  9.  
  10. VkSwapchainKHR swapchain;
  11. vkAPI.vkCreateSwapchainKHR(device, &info, null, &swapchain);

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

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

总结

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

Vulkan(1)用apispec生成Vulkan库的更多相关文章

  1. Xcode 生成静态库相关设置:

    Xcode 生成静态库相关设置: #Build Setting1. Architectures ------- Architectures -----> $(ARCHS_STANDARD) -- ...

  2. 在Linux下如何使用GCC编译程序、简单生成 静态库及动态库

      最近在编写的一个Apache  kafka 的C/C++客户端,,在看他写的 example中,他的编译是用librdkafka++.a和librdkafka.a    静态库编译的,,,而我们这 ...

  3. [转]Linux下用gcc/g++生成静态库和动态库(Z)

    Linux下用gcc/g++生成静态库和动态库(Z) 2012-07-24 16:45:10|  分类: linux |  标签:链接库  linux  g++  gcc  |举报|字号 订阅     ...

  4. Android studio 下JNI编程实例并生成so库

    Android studio 下JNI编程实例并生成so库 因为公司需要为Android相机做美颜等图像后期处理,需要使用JNI编程,最近学了下JNI,并且在Android Studio下实现了一个小 ...

  5. 由动态库文件dll生成lib库文件(手动生成.def文件,然后使用lib命令编译,非常牛),同理可使用dll生成.a库文件

    本文基于OpenBlas的编译和安装,来说明如何从一个dll文件生成lib库文件. 参考OpenBlas的说明“Howto generate import library for MingW”,和Mi ...

  6. Android NDK入门实例 计算斐波那契数列二生成.so库文件

    上一篇文章输生成了jni头文件,里面包含了本地C代码的信息,提供我们引用的C头文件.下面实现本地代码,再用ndk-build编译生成.so库文件.由于编译时要用到make和gcc,这里很多人是通过安装 ...

  7. Linux生成动态库系统

    Linux生成动态库系统 一个.说明 Linux下动态库文件的扩展名为 ".so"(Shared Object). 依照约定,全部动态库文件名称的形式是libname.so(可能在 ...

  8. Ubuntu下通过makefile生成静态库和动态库简单实例

    本文转自http://blog.csdn.net/fengbingchun/article/details/17994489 Ubuntu环境:14.04 首先创建一个test_makefile_gc ...

  9. 如何使用 VS生成动态库?

    如何使用 VS生成动态库? //.cpp 文件默认定义了 __cplusplus 宏 #ifdef __cplusplus extern "C"{ #endif xxx #ifde ...

随机推荐

  1. (数据科学学习手札66)在ubuntu服务器上部署shiny

    一.简介 shiny是R中专门用于开发轻量级web应用的框架,在本地写一个shiny应用并调用非常方便,但如果你希望你的shiny应用能够以远程的方式提供给更多人来使用,就需要将写好的shiny应用部 ...

  2. 两个 github 账号混用,一个帐号提交错误

    问题是这样,之前有一个github帐号,因为注册邮箱的原因,不打算继续使用了,换了一个新的邮箱注册了一个新的邮箱帐号.新账号提交 就会出现下图的问题,但是原来帐号的库还是能正常提交.   方法1:添加 ...

  3. 世界十大OTA公司盘点

    世界十大OTA公司盘点 文/刘照慧(执惠旅游联合创始人,首发百度百家) 全球在线旅游公司(OTA)经过多年发展,已经形成较为成熟的商业模式,各大巨头跑马圈地,格局初现, 这两篇文章就梳理出全球按市值( ...

  4. android ——Toolbar

    Toolbar是我看material design内容的第一个 官方文档:https://developer.android.com/reference/android/support/v7/widg ...

  5. 解读 PHP 的 P++提案

    解读 PHP 的 P++提案 周末看到一篇文章说 PHP 创始人提议将 PHP 拉出新分支,创建 P++ 语言.随后阅读了一下 Zeev Suraski 发起的这个邮件列表,大致了解了一下,这里做个解 ...

  6. python对常见数据类型的遍历

    本文将通过for ... in ...的语法结构,遍历字符串.列表.元组.字典等数据结构. 字符串遍历 >>> a_str = "hello itcast" &g ...

  7. vs2013 在按F5调试时,总是提示 “项目已经过期”的解决方案

    这个是由于缺少某些文件(如.h,xxx.icon),或者文件时间不对 引起的. 如图在工具选项设置 最小为 “诊断”. 然后编译一下,会提示 xxx过期,确认下即可.

  8. Element-UI 2.4.11 版本 使用注意(发现一点更新一点)

    1.$Vue.$refs.addForm.resetFields() 的resetFields()方法重置到默认值并不是 ,你在form绑定对象上写的默认值 ,而是这个form被渲染出来之后第一次赋到 ...

  9. pycharm---文件名颜色所代表的含义

    绿色,已经加入版本控制暂未提交: 红色,未加入版本控制: 蓝色,加入版本控制,已提交,有改动: 白色,加入版本控制,已提交,无改动: 灰色:版本控制已忽略文件.

  10. 初学html总结

    2019-08-17 17:58:49 html:超文本标记语言,用于网页结构的搭建 html语言构成:由标签.属性.属性值构成 标签:" < "后面第一个单词 属性:标签后 ...