blob: 11ec697b0bd2911dc5225cebca038f1e8180432e [file] [log] [blame]
Jesse Hall1f91d392015-12-11 16:28:44 -08001{{/*
2 * Copyright 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */}}
16
17{{Include "../api/templates/vulkan_common.tmpl"}}
18{{Global "clang-format" (Strings "clang-format" "-style=file")}}
19{{Macro "DefineGlobals" $}}
20{{$ | Macro "dispatch_gen.h" | Format (Global "clang-format") | Write "dispatch_gen.h" }}
21{{$ | Macro "dispatch_gen.cpp" | Format (Global "clang-format") | Write "dispatch_gen.cpp"}}
22
23{{/*
24-------------------------------------------------------------------------------
25 dispatch_gen.h
26-------------------------------------------------------------------------------
27*/}}
28{{define "dispatch_gen.h"}}
29/*
30•* Copyright 2015 The Android Open Source Project
31•*
32•* Licensed under the Apache License, Version 2.0 (the "License");
33•* you may not use this file except in compliance with the License.
34•* You may obtain a copy of the License at
35•*
36•* http://www.apache.org/licenses/LICENSE-2.0
37•*
38•* Unless required by applicable law or agreed to in writing, software
39•* distributed under the License is distributed on an "AS IS" BASIS,
40•* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
41•* See the License for the specific language governing permissions and
42•* limitations under the License.
43•*/
44
Jesse Hall1f91d392015-12-11 16:28:44 -080045#define VK_USE_PLATFORM_ANDROID_KHR
46#include <vulkan/vk_android_native_buffer.h>
47#include <vulkan/vulkan.h>
48
49namespace vulkan {
50
51struct InstanceDispatchTable
52 // clang-format off
53 {{range $f := AllCommands $}}
54 {{if (Macro "IsInstanceDispatched" $f)}}
55 {{Macro "FunctionPtrName" $f}} {{Macro "BaseName" $f}};
56 {{end}}
57 {{end}}
58 // clang-format on
59»};
60
61struct DeviceDispatchTable
62 // clang-format off
63 {{range $f := AllCommands $}}
64 {{if (Macro "IsDeviceDispatched" $f)}}
65 {{Macro "FunctionPtrName" $f}} {{Macro "BaseName" $f}};
66 {{end}}
67 {{end}}
68 // clang-format on
69»};
70
71struct DriverDispatchTable
72 // clang-format off
73 {{range $f := AllCommands $}}
74 {{if (Macro "IsInstanceDispatched" $f)}}
75 {{if not (Macro "IsLoaderFunction" $f)}}
76 {{Macro "FunctionPtrName" $f}} {{Macro "BaseName" $f}};
77 {{end}}
78 {{end}}
79 {{end}}
80
81 PFN_vkGetDeviceProcAddr GetDeviceProcAddr;
82
83 {{/* TODO(jessehall): Needed by swapchain code. Figure out a better way of
84 handling this that avoids the special case. Probably should rework
85 things so the driver dispatch table has all driver functions. Probably
86 need separate instance- and device-level copies, fill in all device-
87 dispatched functions in the device-level copies only, and change
88 GetDeviceProcAddr_Bottom to look in the already-loaded driver
89 dispatch table rather than forwarding to the driver's
90 vkGetDeviceProcAddr. */}}
91 PFN_vkCreateImage CreateImage;
92 PFN_vkDestroyImage DestroyImage;
93
94 PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
95 PFN_vkAcquireImageANDROID AcquireImageANDROID;
96 PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
97 // clang-format on
98»};
99
100} // namespace vulkan
101¶{{end}}
102
103
104{{/*
105-------------------------------------------------------------------------------
106 dispatch_gen.cpp
107-------------------------------------------------------------------------------
108*/}}
109{{define "dispatch_gen.cpp"}}
110/*
111•* Copyright 2015 The Android Open Source Project
112•*
113•* Licensed under the Apache License, Version 2.0 (the "License");
114•* you may not use this file except in compliance with the License.
115•* You may obtain a copy of the License at
116•*
117•* http://www.apache.org/licenses/LICENSE-2.0
118•*
119•* Unless required by applicable law or agreed to in writing, software
120•* distributed under the License is distributed on an "AS IS" BASIS,
121•* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
122•* See the License for the specific language governing permissions and
123•* limitations under the License.
124•*/
125
126#include <log/log.h>
127#include <algorithm>
128#include "loader.h"
129
130#define UNLIKELY(expr) __builtin_expect((expr), 0)
131
132using namespace vulkan;
133
134namespace {
135
136struct NameProc {
137 const char* name;
138 PFN_vkVoidFunction proc;
139};
140
141PFN_vkVoidFunction Lookup(const char* name, const NameProc* begin, const NameProc* end) {
142 const auto& entry = std::lower_bound(
143 begin, end, name,
144 [](const NameProc& e, const char* n) { return strcmp(e.name, n) < 0; });
145 if (entry == end || strcmp(entry->name, name) != 0)
146 return nullptr;
147 return entry->proc;
148}
149
150template <size_t N>
151PFN_vkVoidFunction Lookup(const char* name, const NameProc (&procs)[N]) {
152 return Lookup(name, procs, procs + N);
153}
154
155const NameProc kLoaderExportProcs[] =
156 // clang-format off
157 {{range $f := SortBy (AllCommands $) "FunctionName"}}
158 {{if (Macro "IsFunctionSupported" $f)}}
159 {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>({{$f.Name}})},
160 {{end}}
161 {{end}}
162 // clang-format on
163»};
164
165const NameProc kLoaderGlobalProcs[] =
166 // clang-format off
167 {{range $f := SortBy (AllCommands $) "FunctionName"}}
168 {{if and (Macro "HasLoaderTopImpl" $f) (eq (Macro "Vtbl" $f) "Global")}}
169 {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§
170 static_cast<{{Macro "FunctionPtrName" $f}}>(§
171 {{Macro "BaseName" $f}}_Top))},
172 {{end}}
173 {{end}}
174 // clang-format on
175»};
176
177const NameProc kLoaderTopProcs[] =
178 // clang-format off
179 {{range $f := SortBy (AllCommands $) "FunctionName"}}
180 {{if (Macro "HasLoaderTopImpl" $f)}}
181 {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§
182 static_cast<{{Macro "FunctionPtrName" $f}}>(§
183 {{Macro "BaseName" $f}}_Top))},
184 {{end}}
185 {{end}}
186 // clang-format on
187»};
188
189const NameProc kLoaderBottomProcs[] =
190 // clang-format off
191 {{range $f := SortBy (AllCommands $) "FunctionName"}}
192 {{if (Macro "HasLoaderBottomImpl" $f)}}
193 {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§
194 static_cast<{{Macro "FunctionPtrName" $f}}>(§
195 {{Macro "BaseName" $f}}_Bottom))},
196 {{end}}
197 {{end}}
198 // clang-format on
199»};
200
201struct NameOffset {
202 const char* name;
203 size_t offset;
204};
205
206ssize_t Lookup(const char* name,
207 const NameOffset* begin,
208 const NameOffset* end) {
209 const auto& entry = std::lower_bound(
210 begin, end, name, [](const NameOffset& e, const char* n) {
211 return strcmp(e.name, n) < 0;
212 });
213 if (entry == end || strcmp(entry->name, name) != 0)
214 return -1;
215 return static_cast<ssize_t>(entry->offset);
216}
217
218template <size_t N, class Table>
219PFN_vkVoidFunction Lookup(const char* name,
220 const NameOffset (&offsets)[N],
221 const Table& table) {
222 ssize_t offset = Lookup(name, offsets, offsets + N);
223 if (offset < 0)
224 return nullptr;
225 uintptr_t base = reinterpret_cast<uintptr_t>(&table);
226 return *reinterpret_cast<PFN_vkVoidFunction*>(base +
227 static_cast<size_t>(offset));
228}
229
230const NameOffset kInstanceDispatchOffsets[] =
231 // clang-format off
232 {{range $f := SortBy (AllCommands $) "FunctionName"}}
233 {{if (Macro "IsInstanceDispatched" $f)}}
234 {"{{$f.Name}}", offsetof(InstanceDispatchTable, {{Macro "BaseName" $f}})},
235 {{end}}
236 {{end}}
237 // clang-format on
238»};
239
240const NameOffset kDeviceDispatchOffsets[] =
241 // clang-format off
242 {{range $f := SortBy (AllCommands $) "FunctionName"}}
243 {{if (Macro "IsDeviceDispatched" $f)}}
244 {"{{$f.Name}}", offsetof(DeviceDispatchTable, {{Macro "BaseName" $f}})},
245 {{end}}
246 {{end}}
247 // clang-format on
248»};
249
250} // anonymous namespace
251
252namespace vulkan {
253
254PFN_vkVoidFunction GetLoaderExportProcAddr(const char* name) {
255 return Lookup(name, kLoaderExportProcs);
256}
257
258PFN_vkVoidFunction GetLoaderGlobalProcAddr(const char* name) {
259 return Lookup(name, kLoaderGlobalProcs);
260}
261
262PFN_vkVoidFunction GetLoaderTopProcAddr(const char* name) {
263 return Lookup(name, kLoaderTopProcs);
264}
265
266PFN_vkVoidFunction GetLoaderBottomProcAddr(const char* name) {
267 return Lookup(name, kLoaderBottomProcs);
268}
269
270PFN_vkVoidFunction GetDispatchProcAddr(const InstanceDispatchTable& dispatch,
271 const char* name) {
272 return Lookup(name, kInstanceDispatchOffsets, dispatch);
273}
274
275PFN_vkVoidFunction GetDispatchProcAddr(const DeviceDispatchTable& dispatch,
276 const char* name) {
277 return Lookup(name, kDeviceDispatchOffsets, dispatch);
278}
279
280bool LoadInstanceDispatchTable(VkInstance instance,
281 PFN_vkGetInstanceProcAddr get_proc_addr,
282 InstanceDispatchTable& dispatch)
283 bool success = true;
284 // clang-format off
285 {{range $f := AllCommands $}}
286 {{if (Macro "IsInstanceDispatched" $f)}}
287 dispatch.{{Macro "BaseName" $f}} = §
288 reinterpret_cast<{{Macro "FunctionPtrName" $f}}>(§
289 get_proc_addr(instance, "{{$f.Name}}"));
290 if (UNLIKELY(!dispatch.{{Macro "BaseName" $f}})) {
291 ALOGE("missing instance proc: %s", "{{$f.Name}}");
292 success = false;
293 }
294 {{end}}
295 {{end}}
296 // clang-format on
297 return success;
298»}
299
300bool LoadDeviceDispatchTable(VkDevice device,
301 PFN_vkGetDeviceProcAddr get_proc_addr,
302 DeviceDispatchTable& dispatch)
303 bool success = true;
304 // clang-format off
305 {{range $f := AllCommands $}}
306 {{if (Macro "IsDeviceDispatched" $f)}}
307 dispatch.{{Macro "BaseName" $f}} = §
308 reinterpret_cast<{{Macro "FunctionPtrName" $f}}>(§
309 get_proc_addr(device, "{{$f.Name}}"));
310 if (UNLIKELY(!dispatch.{{Macro "BaseName" $f}})) {
311 ALOGE("missing device proc: %s", "{{$f.Name}}");
312 success = false;
313 }
314 {{end}}
315 {{end}}
316 // clang-format on
317 return success;
318»}
319
320bool LoadDriverDispatchTable(VkInstance instance,
321 PFN_vkGetInstanceProcAddr get_proc_addr,
Jesse Hall6bd5dfa2016-01-16 17:13:30 -0800322 const InstanceExtensionSet& extensions,
Jesse Hall1f91d392015-12-11 16:28:44 -0800323 DriverDispatchTable& dispatch)
324 bool success = true;
325 // clang-format off
326 {{range $f := AllCommands $}}
327 {{if (Macro "IsInstanceDispatched" $f)}}
328 {{if not (Macro "IsLoaderFunction" $f)}}
Jesse Hall6bd5dfa2016-01-16 17:13:30 -0800329 {{$ext := GetAnnotation $f "extension"}}
330 {{if $ext}}
331 if (extensions[{{Macro "ExtensionConstant" $ext}}]) {
332 {{end}}
333 dispatch.{{Macro "BaseName" $f}} = §
334 reinterpret_cast<{{Macro "FunctionPtrName" $f}}>(§
335 get_proc_addr(instance, "{{$f.Name}}"));
336 if (UNLIKELY(!dispatch.{{Macro "BaseName" $f}})) {
337 ALOGE("missing driver proc: %s", "{{$f.Name}}");
338 success = false;
339 }
340 {{if $ext}}
Jesse Hall1f91d392015-12-11 16:28:44 -0800341 }
Jesse Hall6bd5dfa2016-01-16 17:13:30 -0800342 {{end}}
Jesse Hall1f91d392015-12-11 16:28:44 -0800343 {{end}}
344 {{end}}
345 {{end}}
346 dispatch.GetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(get_proc_addr(instance, "vkGetDeviceProcAddr"));
347 if (UNLIKELY(!dispatch.GetDeviceProcAddr)) {
348 ALOGE("missing driver proc: %s", "vkGetDeviceProcAddr");
349 success = false;
350 }
351 dispatch.CreateImage = reinterpret_cast<PFN_vkCreateImage>(get_proc_addr(instance, "vkCreateImage"));
352 if (UNLIKELY(!dispatch.CreateImage)) {
353 ALOGE("missing driver proc: %s", "vkCreateImage");
354 success = false;
355 }
356 dispatch.DestroyImage = reinterpret_cast<PFN_vkDestroyImage>(get_proc_addr(instance, "vkDestroyImage"));
357 if (UNLIKELY(!dispatch.DestroyImage)) {
358 ALOGE("missing driver proc: %s", "vkDestroyImage");
359 success = false;
360 }
Jesse Halld9132822016-01-14 15:50:52 -0800361 dispatch.GetSwapchainGrallocUsageANDROID = reinterpret_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(get_proc_addr(instance, "vkGetSwapchainGrallocUsageANDROID"));
362 if (UNLIKELY(!dispatch.GetSwapchainGrallocUsageANDROID)) {
363 ALOGE("missing driver proc: %s", "vkGetSwapchainGrallocUsageANDROID");
364 success = false;
365 }
Jesse Hall1f91d392015-12-11 16:28:44 -0800366 dispatch.AcquireImageANDROID = reinterpret_cast<PFN_vkAcquireImageANDROID>(get_proc_addr(instance, "vkAcquireImageANDROID"));
367 if (UNLIKELY(!dispatch.AcquireImageANDROID)) {
368 ALOGE("missing driver proc: %s", "vkAcquireImageANDROID");
369 success = false;
370 }
371 dispatch.QueueSignalReleaseImageANDROID = reinterpret_cast<PFN_vkQueueSignalReleaseImageANDROID>(get_proc_addr(instance, "vkQueueSignalReleaseImageANDROID"));
372 if (UNLIKELY(!dispatch.QueueSignalReleaseImageANDROID)) {
373 ALOGE("missing driver proc: %s", "vkQueueSignalReleaseImageANDROID");
374 success = false;
375 }
376 // clang-format on
377 return success;
378»}
379
380} // namespace vulkan
381
382// clang-format off
383
384{{range $f := AllCommands $}}
385 {{if and (not (GetAnnotation $f "pfn")) (Macro "IsExported" $f)}}
386 __attribute__((visibility("default")))
387 VKAPI_ATTR {{Node "Type" $f.Return}} {{$f.Name}}({{Macro "Parameters" $f}}) {
388 {{if not (IsVoid $f.Return.Type)}}return §{{end}}
389 {{Macro "Dispatch" $f}}({{Macro "Arguments" $f}});
390 }
391
392 {{end}}
393{{end}}
394
395// clang-format on
396¶{{end}}
397
398
399{{/*
400-------------------------------------------------------------------------------
401 Emit the dispatch lookup for a function based on its first parameter.
402-------------------------------------------------------------------------------
403*/}}
404{{define "Dispatch"}}
405 {{AssertType $ "Function"}}
406
407 {{if (Macro "HasLoaderTopImpl" $)}}
408 {{Macro "BaseName" $}}_Top§
409 {{else}}
410 {{$p0 := index $.CallParameters 0}}
411 GetDispatchTable({{$p0.Name}}).{{Macro "BaseName" $}}§
412 {{end}}
413{{end}}
414
415
416{{/*
417-------------------------------------------------------------------------------
Jesse Hall6bd5dfa2016-01-16 17:13:30 -0800418 Map an extension name to InstanceExtension or DeviceExtension enum value
419-------------------------------------------------------------------------------
420*/}}
421{{define "ExtensionConstant"}}
422 {{$name := index $.Arguments 0}}
423 {{ if (eq $name "VK_KHR_surface")}}kKHR_surface
424 {{else if (eq $name "VK_KHR_android_surface")}}kKHR_android_surface
425 {{else if (eq $name "VK_EXT_debug_report")}}kEXT_debug_report
426 {{end}}
427{{end}}
428
429
430{{/*
431-------------------------------------------------------------------------------
Jesse Hall1f91d392015-12-11 16:28:44 -0800432 Emits a function name without the "vk" prefix.
433-------------------------------------------------------------------------------
434*/}}
435{{define "BaseName"}}
436 {{AssertType $ "Function"}}
437 {{TrimPrefix "vk" $.Name}}
438{{end}}
439
440
441{{/*
442-------------------------------------------------------------------------------
443 Emits a comma-separated list of C parameter names for the given command.
444-------------------------------------------------------------------------------
445*/}}
446{{define "Arguments"}}
447 {{AssertType $ "Function"}}
448
449 {{ForEach $.CallParameters "ParameterName" | JoinWith ", "}}
450{{end}}
451
452
453{{/*
454------------------------------------------------------------------------------
455 Emit "true" for supported functions that undergo table dispatch. Only global
456 functions and functions handled in the loader top without calling into
457 lower layers are not dispatched.
458------------------------------------------------------------------------------
459*/}}
460{{define "IsInstanceDispatched"}}
461 {{AssertType $ "Function"}}
462 {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Instance")}}
463 {{if (ne $.Name "vkGetInstanceProcAddr")}}true{{end}}
464 {{end}}
465{{end}}
466
467
468{{/*
469------------------------------------------------------------------------------
470 Emit "true" for supported functions that can have device-specific dispatch.
471------------------------------------------------------------------------------
472*/}}
473{{define "IsDeviceDispatched"}}
474 {{AssertType $ "Function"}}
475 {{if (Macro "IsFunctionSupported" $)}}
476 {{if eq (Macro "Vtbl" $) "Device"}}
477 {{if ne $.Name "vkGetDeviceProcAddr"}}
478 true
479 {{end}}
480 {{end}}
481 {{end}}
482{{end}}
483
484
485{{/*
486------------------------------------------------------------------------------
487 Emit "true" if a function is core or from a supportable extension.
488------------------------------------------------------------------------------
489*/}}
490{{define "IsFunctionSupported"}}
491 {{AssertType $ "Function"}}
492 {{if not (GetAnnotation $ "pfn")}}
493 {{$ext := GetAnnotation $ "extension"}}
494 {{if not $ext}}true
495 {{else if not (Macro "IsExtensionBlacklisted" $ext)}}true
496 {{end}}
497 {{end}}
498{{end}}
499
500
501{{/*
502------------------------------------------------------------------------------
503 Decides whether a function should be exported from the Android Vulkan
504 library. Functions in the core API and in loader extensions are exported.
505------------------------------------------------------------------------------
506*/}}
507{{define "IsExported"}}
508 {{AssertType $ "Function"}}
509
510 {{$ext := GetAnnotation $ "extension"}}
511 {{if $ext}}
512 {{Macro "IsLoaderExtension" $ext}}
513 {{else}}
514 true
515 {{end}}
516{{end}}
517
518
519{{/*
520------------------------------------------------------------------------------
521 Reports whether an extension function is implemented entirely by the loader,
522 and not implemented by drivers.
523------------------------------------------------------------------------------
524*/}}
525{{define "IsLoaderFunction"}}
526 {{AssertType $ "Function"}}
527
528 {{$ext := GetAnnotation $ "extension"}}
529 {{if $ext}}
530 {{Macro "IsLoaderExtension" $ext}}
531 {{end}}
532{{end}}
533
534
535{{/*
536-------------------------------------------------------------------------------
537 Emit "true" if the loader has a top-level implementation for the function
538 that should be called directly rather than dispatching to the first layer.
539-------------------------------------------------------------------------------
540*/}}
541{{define "HasLoaderTopImpl"}}
542 {{AssertType $ "Function"}}
543
544 {{/* Global functions can't be dispatched */}}
545 {{ if and (not (GetAnnotation $ "pfn")) (eq (Macro "Vtbl" $) "Global")}}true
546
547 {{/* G*PA are implemented by reading the dispatch table, not by dispatching
548 through it. */}}
549 {{else if eq $.Name "vkGetInstanceProcAddr"}}true
550 {{else if eq $.Name "vkGetDeviceProcAddr"}}true
551
552 {{/* Loader top needs to initialize dispatch for device-level dispatchable
553 objects */}}
554 {{else if eq $.Name "vkGetDeviceQueue"}}true
555 {{else if eq $.Name "vkAllocateCommandBuffers"}}true
556
557 {{/* vkDestroy for dispatchable objects needs to handle VK_NULL_HANDLE;
558 trying to dispatch through that would crash. */}}
559 {{else if eq $.Name "vkDestroyInstance"}}true
560 {{else if eq $.Name "vkDestroyDevice"}}true
561
562 {{end}}
563{{end}}
564
565
566{{/*
567-------------------------------------------------------------------------------
568 Emit "true" if the loader has a bottom-level implementation for the function
569 which terminates the dispatch chain.
570-------------------------------------------------------------------------------
571*/}}
572{{define "HasLoaderBottomImpl"}}
573 {{AssertType $ "Function"}}
574
575 {{if (Macro "IsFunctionSupported" $)}}
576 {{ if (eq (Macro "Vtbl" $) "Instance")}}true
577 {{else if (Macro "IsLoaderFunction" $)}}true
578 {{else if (eq $.Name "vkCreateInstance")}}true
579 {{else if (eq $.Name "vkGetDeviceProcAddr")}}true
580 {{end}}
581 {{end}}
582{{end}}
583
584
585{{/*
586------------------------------------------------------------------------------
587 Emit "true" if an extension is unsupportable on Android.
588------------------------------------------------------------------------------
589*/}}
590{{define "IsExtensionBlacklisted"}}
591 {{$ext := index $.Arguments 0}}
592 {{ if eq $ext "VK_KHR_display"}}true
593 {{else if eq $ext "VK_KHR_display_swapchain"}}true
594 {{else if eq $ext "VK_KHR_xlib_surface"}}true
595 {{else if eq $ext "VK_KHR_xcb_surface"}}true
596 {{else if eq $ext "VK_KHR_wayland_surface"}}true
597 {{else if eq $ext "VK_KHR_mir_surface"}}true
598 {{else if eq $ext "VK_KHR_win32_surface"}}true
599 {{end}}
600{{end}}
601
602
603{{/*
604------------------------------------------------------------------------------
605 Reports whether an extension is implemented entirely by the loader,
606 so drivers should not enumerate it.
607------------------------------------------------------------------------------
608*/}}
609{{define "IsLoaderExtension"}}
610 {{$ext := index $.Arguments 0}}
611 {{ if eq $ext "VK_KHR_surface"}}true
612 {{else if eq $ext "VK_KHR_swapchain"}}true
613 {{else if eq $ext "VK_KHR_android_surface"}}true
614 {{end}}
615{{end}}