1 module sbylib.wrapper.vulkan.util;
2 
3 import std;
4 import erupted;
5 import sbylib.wrapper.vulkan;
6 
7 struct VkProp {
8     string[] names;
9 }
10 
11 VkProp vkProp(string[] names...) {
12     return VkProp(names);
13 }
14 
15 // ex) uint -> uint
16 Dst convFrom(Dst)(Dst t) if (isBasicType!Dst) {
17     return t;
18 }
19 
20 // ex) VkMemoryType -> VkMemoryType
21 Dst convFrom(Dst)(Dst t) if (is(Dst == struct)) {
22     return t;
23 }
24 
25 // ex) char[256] -> immutable(string)
26 Dst convFrom(Dst: string, uint N)(char[N] src) {
27     return fromStringz(src.ptr).idup;
28 }
29 
30 // ex) uint -> BitFlags!Flags
31 Dst convFrom(Dst, Src)(Src src) if (isInstanceOf!(BitFlags, Dst)) {
32     Dst result;
33     static if (is(Dst == BitFlags!Enum, Enum)) {
34         static foreach (mem; EnumMembers!Enum) {
35             if (src & mem) {
36                 result |= mem;
37             }
38         }
39     } else {
40         static assert(false);
41     }
42     return result;
43 }
44 
45 // ex) (VkMemoryType[32], uint) -> const(VkMemoryType)[]
46 Dst construct(Dst, Src, uint N, Length)(Src[N] src, Length len) if (isDynamicArray!(Dst))
47     in (len < N)
48 {
49     alias DstE = Unqual!(ForeachType!(Dst));
50 
51     DstE[] result = new DstE[len];
52     foreach (i; 0..len) {
53         result[i] = convFrom!DstE(src[i]);
54     }
55     return result;
56 }
57 
58 // ex) VkMemoryHeap -> MemoryHeap
59 Dst convFrom(Dst, Src)(Src src) if (is(Src == struct) && is(Dst == struct) && !is(Src == Dst)) {
60     return Dst(src);
61 }
62 
63 // ex) VkSurfaceTransformFlagBitsKHR -> SurfaceTransform
64 Dst convFrom(Dst, Src)(Src src) if (is(Src == enum) && is(Dst == enum) && !is(Src == Dst)) {
65     return src.to!(OriginalType!Src).to!(Dst);
66 }
67 
68 // ex) uint -> uint
69 Dst convTo(Dst)(Dst t) if (isBasicType!Dst) {
70     return t;
71 }
72 
73 // ex) VkRect2D -> VkRect2D
74 Dst convTo(Dst)(Dst t) if (is(Dst == struct)) {
75     return t;
76 }
77 
78 // ex) VkClearValue -> VkClearValue
79 Dst convTo(Dst)(Dst t) if (is(Dst == union)) {
80     return t;
81 }
82 
83 // ex) const(VkPhysicalDeviceFeatures)* -> const(VkPhysicalDeviceFeatures*)
84 Dst convTo(Dst)(Dst t) if (isPointer!Dst) {
85     return t;
86 }
87 
88 // ex) immutable(float[4]) -> float[4]
89 Dst convTo(Dst)(Dst t) if (isStaticArray!Dst) {
90     return t;
91 }
92 
93 // ex) CommandPool -> VkCommandPool
94 Dst convTo(Dst, Src)(Src src) if (is(Src == class) && hasMember!(Src, "vkTo") && isPointer!(Dst)) {
95     if (src is null) return null;
96 
97     return src.vkTo();
98 }
99 
100 
101 // ex) CommandBuffer.InheritanceInfo -> VkCommandBufferInheritanceInfo
102 Dst convTo(Dst, Src)(Src src) if (is(Src == struct) && is(Dst == struct) && hasMember!(Src, "vkTo")) {
103     return src.vkTo();
104 }
105 
106 // ex) CommandBuffer.InheritanceInfo -> const(VkCommandBufferInheritanceInfo)*
107 Dst convTo(Dst, Src)(Src src) if (is(Src == struct) && isPointer!(Dst)) {
108     alias DstE = Unqual!(PointerTarget!Dst);
109 
110     DstE* tmp = new DstE;
111     *tmp = convTo!(DstE)(src);
112     return tmp;
113 }
114 
115 // ex) immutable BitFlags!(Flags) -> uint
116 Dst convTo(Dst, Src)(Src src) if (isInstanceOf!(BitFlags, Src)) {
117     static if (is(Src == BitFlags!Enum, Enum)) {
118         return to!Dst(cast(OriginalType!Enum)src);
119     } else {
120         static assert(false);
121     }
122 }
123 
124 // ex) const(string) -> const(char)*
125 Dst convTo(Dst)(const string src) {
126     return src.toStringz();
127 }
128 
129 // ex) const(uint[]) -> (uint*, uint)
130 Tuple!(Dst*, Length) destruct(Dst, Length, Src)(Src[] src) if (is(Src == Dst)) {
131     return tuple(src.ptr, cast(Length) src.length);
132 }
133 
134 // ex) const(QueueCreateInfo[]) -> (VkDeviceQueueCreateInfo*, uint)
135 Tuple!(Dst*, Length) destruct(Dst, Length, Src)(const Src[] src)
136     if (!is(Src == Dst) && !isBasicType!Src && !isBasicType!Dst)
137 {
138     Dst[] dst = new Dst[src.length];
139     foreach (i; 0 .. src.length) {
140         dst[i] = convTo!(Dst)(src[i]);
141     }
142     return tuple(dst.ptr, cast(Length) dst.length);
143 }
144 
145 // ex) ShaderStageCreateInfo -> (VkPipelineShaderStageCreateInfo, uint)
146 Tuple!(Dst*, Length) destruct(Dst, Length, Src)(Src[] src)
147     if (!is(Src == Dst) && !isBasicType!Src && !isBasicType!Dst)
148 {
149     Dst[] dst = new Dst[src.length];
150     foreach (i; 0 .. src.length) {
151         dst[i] = convTo!(Dst)(src[i]);
152     }
153     return tuple(dst.ptr, cast(Length) dst.length);
154 }
155 
156 // ex) ubyte[] -> (uint*, size_t)
157 Tuple!(Dst*, Length) destruct(Dst, Length, Src)(Src[] src)
158     if (!is(Src == Dst) && isBasicType!Src && isBasicType!Dst)
159 {
160     return tuple(cast(Dst*)src.ptr, cast(Length) src.length);
161 }
162 
163 mixin template VkFrom(OriginalType) {
164     import std;
165     import sbylib.wrapper.vulkan.util;
166 
167     this(OriginalType orig) {
168         static foreach (name; VkPropertyNames!(typeof(this))) {
169             {
170                 alias Dst = Unqual!(typeof(mixin("this.", name)));
171                 alias props = getUDAs!(mixin("this.", name), VkProp);
172                 static assert(props.length == 1);
173                 enum names = props[0].names;
174 
175                 static if (names.length > 0) {
176                     auto tmp = mixin("tuple(", names.map!(n => "orig." ~ n).join(","), ")");
177                     mixin("this.", name) = construct!(Dst)(tmp.expand);
178                 } else {
179                     mixin("this.", name) = convFrom!(Dst)(mixin("orig.", name));
180                 }
181             }
182         }
183     }
184 }
185 
186 mixin template VkTo(OriginalType) {
187     import std;
188     import sbylib.wrapper.vulkan.util;
189 
190     template hasOriginalType(string[] members) {
191         static if (members.length == 0)
192             enum hasOriginalType = false;
193         else static if (is(typeof(__traits(getMember, typeof(this), members[0])) == OriginalType))
194             enum hasOriginalType = true;
195         else
196             enum hasOriginalType = hasOriginalType!(members[1 .. $]);
197     }
198 
199     static if (hasOriginalType!([__traits(allMembers, typeof(this))])) {
200         inout(OriginalType) vkTo() inout {
201             static foreach (mem; FieldNameTuple!(typeof(this))) {
202                 static if (is(typeof(mixin("this.", mem)) == inout(OriginalType))) {
203                     return mixin("this.", mem);
204                 }
205             }
206         }
207     } else {
208         OriginalType vkTo() {
209             OriginalType orig;
210             static foreach (name; VkPropertyNames!(typeof(this))) {
211                 {
212                     alias Src = typeof(mixin("this.", name));
213 
214                     alias props = getUDAs!(mixin("this.", name), VkProp);
215                     static assert(props.length == 1);
216 
217                     enum names = props[0].names;
218 
219                     static if (names.length == 2) {
220                         static assert(isArray!(Src));
221 
222                         alias DstPtr = typeof(mixin("orig.", names[0]));
223                         alias Length = typeof(mixin("orig.", names[1]));
224 
225                         static assert(isPointer!(DstPtr));
226                         alias Dst = Unqual!(PointerTarget!(DstPtr));
227 
228                         auto tmp = destruct!(Dst, Length)(mixin("this.", name));
229                         mixin("orig.", names[0]) = tmp[0];
230 
231                         if (tmp[1] > 0 && mixin("orig.", names[1]) == 0) {
232                             mixin("orig.", names[1]) = tmp[1]; // for avoiding double assignment
233                         }
234                     } else static if (names.length == 1) {
235                         alias Dst = typeof(mixin("orig.", names[0]));
236                         static if (isDynamicArray!(Src) && isPointer!(Dst)) {
237                             auto tmp = destruct!(Unqual!(PointerTarget!Dst), uint)(mixin("this.", name));
238                             mixin("orig.", names[0]) = tmp[0];
239                         } else {
240                             mixin("orig.", names[0]) = convTo!(Dst)(mixin("this.", name));
241                         }
242                     } else static if (names.length == 0) {
243                         alias Dst = typeof(mixin("orig.", name));
244                         mixin("orig.", name) = convTo!(Dst)(mixin("this.", name));
245                     } else {
246                         static assert(false);
247                     }
248                 }
249             }
250             return orig;
251         }
252     }
253 }
254 
255 mixin template ImplToString() {
256     import std;
257     import std : format;
258 
259     string toString() const {
260 
261         enum names = VkPropertyNames!(typeof(this));
262 
263         enum L = names.map!(n => n.length).maxElement;
264 
265         string[] fields;
266         static foreach (name; names) {
267             {
268                 static if (is(Unqual!(typeof(mixin("this.", name))) == BitFlags!E, E)) {
269                     fields ~= format!("%" ~ L.to!string ~ "s: %s")(name,
270                             [EnumMembers!E].filter!(m => cast(bool)(mixin("this.", name) & m))
271                             .map!(to!string)
272                             .join(" | "));
273                 } else {
274                     fields ~= format!("%" ~ L.to!string ~ "s: %s")(name,
275                             mixin("this.", name).to!string);
276                 }
277             }
278         }
279 
280         return format!"%s {\n%s\n}"(Unqual!(typeof(this)).stringof,
281                 fields.map!(txt => "  " ~ txt).join("\n"));
282     }
283 }
284 
285 template VkPropertyNames(This) {
286     import std;
287 
288     private enum isVkProp(string name) = hasUDA!(__traits(getMember, This, name), VkProp);
289     static immutable VkPropertyNames = [
290         Filter!(isVkProp, FieldNameTuple!(This))
291     ];
292 }
293 
294 void enforceVK(VkResult res) {
295     enforce(res == VkResult.VK_SUCCESS, res.to!string);
296 }
297 
298 void compileShader(string filename) {
299     const result = executeShell("glslangValidator -e main -V " ~ filename);
300     assert(result.status == 0, result.output);
301 }
302 
303 mixin template ImplNameSetter(alias device, alias obj, DebugReportObjectType type) {
304     import std : toStringz;
305 
306     void name(string n) {
307         VkDebugMarkerObjectNameInfoEXT info = {
308             objectType: type,
309             object: cast(ulong)obj,
310             pObjectName: n.toStringz()
311         };
312         enforceVK(vkDebugMarkerSetObjectNameEXT(device.device, &info));
313     }
314 }