Vulkan(1)用apispec生成Vulkan库

coding

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的描述:

<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"><codeclass="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#代码了。解析代码很简单,就不解释了。

  1using System;

2using System.Collections.Generic;

3using System.Xml.Linq;

4

5namespace ApiSpec {

6class EnumsParser {

7

8staticreadonlychar[] inLineSeparator = newchar[] { '', '\t', '\r', '\n', };

9staticreadonlychar[] lineSeparator = newchar[] { '\r', '\n' };

10conststring leftBrace = "{";

11conststring rightBrace = "}";

12

13conststring filename = "Enums.content.xml";

14conststring strName = "Name";

15conststring strCSpecification = "C Specification";

16conststring strDescription = "Description";

17conststring strSeeAlso = "See Also";

18conststring strDocNotes = "Document Notes";

19

20class 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*/

28publicstring raw;

29

30publicstring[] Dump() {

31string[] lines = this.raw.Split(lineSeparator, StringSplitOptions.RemoveEmptyEntries);

32if (lines == null || lines.Length < 2) { return lines; }

33

34 {

35string[] parts = lines[0].Split(inLineSeparator, StringSplitOptions.RemoveEmptyEntries);

36 lines[0] = $"public enum {parts[2]} {leftBrace}";

37 }

38 {

39int last = lines.Length - 1;

40 lines[last] = $"{rightBrace}";

41 }

42

43return lines;

44 }

45 }

46

47class EnumItemComment {

48public List<string> lstComment = new List<string>();

49

50public Dictionary<string, string> Dump() {

51 Dictionary<string, string> dict = new Dictionary<string, string>();

52foreach (var item in lstComment) {

53int left = item.IndexOf("<code>");

54int right = item.IndexOf("</code>");

55if (left != -1 && right != -1) {

56string key = item.Substring(left + "<code>".Length, right - (left + "<code>".Length));

57if (!dict.ContainsKey(key)) {

58 dict.Add(key, item);

59 }

60 }

61 }

62

63return dict;

64 }

65 }

66

67publicstaticvoid DumpEnums() {

68 XElement root = XElement.Load(filename);

69var lstDefinition = new List<EnumDefinetion>(); bool inside = false;

70 TraverseNodesEnumDefinitions(root, lstDefinition, ref inside);

71var listEnumItemComment = new List<EnumItemComment>(); inside = false;

72 TraverseNodesEnumItemComments(root, listEnumItemComment, ref inside);

73var lstEnumComment = new List<string>(); inside = false;

74 TraverseNodesEnumComments(root, lstEnumComment, ref inside);

75

76using (var sw = new System.IO.StreamWriter("Enums.gen.cs")) {

77for (int i = 0; i < lstDefinition.Count; i++) {

78 EnumDefinetion definition = lstDefinition[i];

79//sw.WriteLine(definition.raw);

80string[] definitionLines = definition.Dump();

81 EnumItemComment itemComment = listEnumItemComment[i];

82 Dictionary<string, string> item2Comment = itemComment.Dump();

83

84 sw.WriteLine($"// Enum: {i}");

85string enumComment = lstEnumComment[i];

86 sw.WriteLine($"/// <summary>{enumComment}</summary>");

87 {

88string line = definitionLines[0];

89if (line.Contains("FlagBits")) { sw.WriteLine("[Flags]"); }

90 sw.WriteLine(line);

91 }

92for (int j = 1; j < definitionLines.Length - 1; j++) {

93string line = definitionLines[j];

94if (item2Comment != null) {

95string strComment = ParseItemComment(line, item2Comment);

96if (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 {

106string 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>*/

118privatestaticvoid TraverseNodesEnumComments(XElement node, List<string> list, refbool inside) {

119if (node.Name == "h4") {

120if (node.Value == "Name") {

121 inside = true;

122 }

123 }

124elseif (node.Name == "p") {

125if (inside) {

126string 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

134foreach (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

143bottom-level level acceleration structures.

144<code>VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV</code> is a bottom-level

145acceleration structure containing the AABBs or geometry to be

146intersected.

147*/

148staticreadonlychar[] equalSeparator = newchar[] { '=', '', '\t', '\r', '\n', };

149privatestaticstring ParseItemComment(string line, Dictionary<string, string> dict) {

150string result = string.Empty;

151string[] parts = line.Split(equalSeparator, StringSplitOptions.RemoveEmptyEntries);

152if (parts.Length == 2) {

153string key = parts[0];

154if (dict.ContainsKey(key)) {

155 result = dict[key];

156 }

157 }

158

159return result;

160 }

161

162///<summary>

163///

164///</summary>

165///<param name="node"></param>

166///<param name="list"></param>

167///<param name="inside"></param>

168privatestaticvoid TraverseNodesEnumItemComments(XElement node, List<EnumItemComment> list, refbool inside) {

169if (node.Name == "h4") {

170if (node.Value == "Description") {

171 inside = true;

172var comment = new EnumItemComment();

173 list.Add(comment);

174 }

175elseif (node.Value == "See Also") {

176 inside = false;

177 }

178 }

179elseif (node.Name == "p") {

180if (inside) {

181 EnumItemComment comment = list[list.Count - 1];

182string 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

189foreach (XElement item in node.Elements()) {

190 TraverseNodesEnumItemComments(item, list, ref inside);

191 }

192 }

193

194

195privatestaticvoid TraverseNodesEnumDefinitions(XElement node, List<EnumDefinetion> list, refbool inside) {

196if (node.Name == "h4") {

197if (node.Value == "C Specification") {

198 inside = true;

199 }

200 }

201elseif (node.Name == "code") {

202if (inside) {

203 XAttribute attrClass = node.Attribute("class");

204if (attrClass != null && attrClass.Value == "language-c++") {

205string v = node.Value;

206var item = new EnumDefinetion() { raw = v, };

207 list.Add(item);

208 inside = false;

209 }

210 }

211 }

212

213foreach (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>

3publicenum 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>

19publicenum 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>

6publicstruct VkBuffer {

7public 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>

15publicstruct VkInstance {

16public 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的说明形式。对于它们,我们分别可以用下面的代码代替:

1using 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>

3publicunsafedelegatevoid* PFN_vkAllocationFunction(

4///<summary>pUserData is the value specified for

5/// VkAllocationCallbacks::pUserData in the allocator specified

6/// by the application.</summary>

7void* 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>

19publicunsafedelegate 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>

48void* pUserData);

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

Struct

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

 1// Struct: 4

2///<summary>VkAllocationCallbacks - Structure containing callback function pointers for memory allocation

3///</summary>

4publicunsafestruct 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>

12publicvoid* pUserData;

13///<summary> pfnAllocation is a pointer to an application-defined memory

14/// allocation function of type PFN_vkAllocationFunction.</summary>

15public/*PFN_vkAllocationFunction*/IntPtr pfnAllocation;

16///<summary> pfnReallocation is a pointer to an application-defined memory

17/// reallocation function of type PFN_vkReallocationFunction.</summary>

18public/*PFN_vkReallocationFunction*/IntPtr pfnReallocation;

19///<summary> pfnFree is a pointer to an application-defined memory free

20/// function of type PFN_vkFreeFunction.</summary>

21public/*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>

26public/*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>

31public/*PFN_vkInternalFreeNotification*/IntPtr pfnInternalFree;

32}

33// Struct: 9

34///<summary>VkApplicationInfo - Structure specifying application info

35///</summary>

36publicunsafestruct VkApplicationInfo {

37///<summary> sType is the type of this structure.</summary>

38public VkStructureType sType;

39///<summary> pNext is NULL or a pointer to an extension-specific structure.</summary>

40public/*-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>

43public IntPtr pApplicationName;

44///<summary> applicationVersion is an unsigned integer variable containing the

45/// developer-supplied version number of the application.</summary>

46public 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>

50public 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>

54public 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>

63public UInt32 apiVersion;

64}

65// Struct: 193

66///<summary>VkInstanceCreateInfo - Structure specifying parameters of a newly created instance

67///</summary>

68publicunsafestruct VkInstanceCreateInfo {

69///<summary> sType is the type of this structure.</summary>

70public VkStructureType sType;

71///<summary> pNext is NULL or a pointer to an extension-specific structure.</summary>

72public/*-const-*/void* pNext;

73///<summary> flags is reserved for future use.</summary>

74public 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>

80public/*-const-*/ VkApplicationInfo* pApplicationInfo;

81///<summary> enabledLayerCount is the number of global layers to enable.</summary>

82public 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>

87public IntPtr /*-const-*/ * ppEnabledLayerNames;

88///<summary> enabledExtensionCount is the number of global extensions to

89/// enable.</summary>

90public 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>

94public 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>

6publicstaticvoid Set(thisstring value, ref IntPtr target) {

7 { // free unmanaged memory.

8if (target != IntPtr.Zero) {

9 Marshal.FreeHGlobal(target);

10 target = IntPtr.Zero;

11 }

12 }

13 {

14if (value != null && value.Length > 0) {

15 target = Marshal.StringToHGlobalAnsi(value);

16 }

17else {

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>

9publicstaticvoid Set<T>(this T[] value, ref IntPtr target, ref UInt32 count) where T : struct {

10 { // free unmanaged memory.

11if (target != IntPtr.Zero) {

12 Marshal.FreeHGlobal(target);

13 target = IntPtr.Zero;

14 count = 0;

15 }

16 }

17 {

18 count = (UInt32)value.Length;

19

20int elementSize = Marshal.SizeOf<T>();

21int byteLength = (int)(count * elementSize);

22 IntPtr array = Marshal.AllocHGlobal(byteLength);

23var dst = (byte*)array;

24 GCHandle pin = GCHandle.Alloc(value, GCHandleType.Pinned);

25 IntPtr address = Marshal.UnsafeAddrOfPinnedArrayElement(value, 0);

26var src = (byte*)address;

27for (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赋予具体类型的指针。例如:

1var deviceInfo = new VkDeviceCreateInfo();

2 IntPtr ptr = IntPtr.Zero;

3new 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)]

14publicstaticextern 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)]

33publicstaticexternvoid 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;

2var 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不会被反复使用,因此,它们最多泄漏一点点内存,不会像服务器代码那样占用越来越多的内存,所以不碍事的。

总结

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

 

原文出处:https://www.cnblogs.com/bitzhuwei/p/Vulkan-1-from-apispec.html

以上是 Vulkan(1)用apispec生成Vulkan库 的全部内容, 来源链接: utcz.com/z/509386.html

回到顶部