| {{/* |
| * Copyright 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */}} |
| |
| {{Include "../api/templates/vulkan_common.tmpl"}} |
| {{Global "clang-format" (Strings "clang-format" "-style=file")}} |
| {{Macro "DefineGlobals" $}} |
| {{$ | Macro "dispatch_gen.h" | Format (Global "clang-format") | Write "dispatch_gen.h" }} |
| {{$ | Macro "dispatch_gen.cpp" | Format (Global "clang-format") | Write "dispatch_gen.cpp"}} |
| |
| {{/* |
| ------------------------------------------------------------------------------- |
| dispatch_gen.h |
| ------------------------------------------------------------------------------- |
| */}} |
| {{define "dispatch_gen.h"}} |
| /* |
| •* Copyright 2015 The Android Open Source Project |
| •* |
| •* Licensed under the Apache License, Version 2.0 (the "License"); |
| •* you may not use this file except in compliance with the License. |
| •* You may obtain a copy of the License at |
| •* |
| •* http://www.apache.org/licenses/LICENSE-2.0 |
| •* |
| •* Unless required by applicable law or agreed to in writing, software |
| •* distributed under the License is distributed on an "AS IS" BASIS, |
| •* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| •* See the License for the specific language governing permissions and |
| •* limitations under the License. |
| •*/ |
| ¶ |
| #define VK_USE_PLATFORM_ANDROID_KHR |
| #include <vulkan/vk_android_native_buffer.h> |
| #include <vulkan/vk_ext_debug_report.h> |
| #include <vulkan/vulkan.h> |
| ¶ |
| namespace vulkan { |
| ¶ |
| struct InstanceDispatchTable {« |
| // clang-format off |
| {{range $f := AllCommands $}} |
| {{if (Macro "IsInstanceDispatched" $f)}} |
| {{Macro "FunctionPtrName" $f}} {{Macro "BaseName" $f}}; |
| {{end}} |
| {{end}} |
| // clang-format on |
| »}; |
| ¶ |
| struct DeviceDispatchTable {« |
| // clang-format off |
| {{range $f := AllCommands $}} |
| {{if (Macro "IsDeviceDispatched" $f)}} |
| {{Macro "FunctionPtrName" $f}} {{Macro "BaseName" $f}}; |
| {{end}} |
| {{end}} |
| // clang-format on |
| »}; |
| ¶ |
| struct DriverDispatchTable {« |
| // clang-format off |
| {{range $f := AllCommands $}} |
| {{if (Macro "IsInstanceDispatched" $f)}} |
| {{if not (Macro "IsLoaderFunction" $f)}} |
| {{Macro "FunctionPtrName" $f}} {{Macro "BaseName" $f}}; |
| {{end}} |
| {{end}} |
| {{end}} |
| |
| PFN_vkGetDeviceProcAddr GetDeviceProcAddr; |
| |
| {{/* TODO(jessehall): Needed by swapchain code. Figure out a better way of |
| handling this that avoids the special case. Probably should rework |
| things so the driver dispatch table has all driver functions. Probably |
| need separate instance- and device-level copies, fill in all device- |
| dispatched functions in the device-level copies only, and change |
| GetDeviceProcAddr_Bottom to look in the already-loaded driver |
| dispatch table rather than forwarding to the driver's |
| vkGetDeviceProcAddr. */}} |
| PFN_vkCreateImage CreateImage; |
| PFN_vkDestroyImage DestroyImage; |
| |
| PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID; |
| PFN_vkAcquireImageANDROID AcquireImageANDROID; |
| PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID; |
| // clang-format on |
| »}; |
| ¶ |
| } // namespace vulkan |
| ¶{{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------- |
| dispatch_gen.cpp |
| ------------------------------------------------------------------------------- |
| */}} |
| {{define "dispatch_gen.cpp"}} |
| /* |
| •* Copyright 2015 The Android Open Source Project |
| •* |
| •* Licensed under the Apache License, Version 2.0 (the "License"); |
| •* you may not use this file except in compliance with the License. |
| •* You may obtain a copy of the License at |
| •* |
| •* http://www.apache.org/licenses/LICENSE-2.0 |
| •* |
| •* Unless required by applicable law or agreed to in writing, software |
| •* distributed under the License is distributed on an "AS IS" BASIS, |
| •* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| •* See the License for the specific language governing permissions and |
| •* limitations under the License. |
| •*/ |
| ¶ |
| #include <log/log.h> |
| #include <algorithm> |
| #include "loader.h" |
| ¶ |
| #define UNLIKELY(expr) __builtin_expect((expr), 0) |
| ¶ |
| using namespace vulkan; |
| ¶ |
| namespace { |
| ¶ |
| struct NameProc { |
| const char* name; |
| PFN_vkVoidFunction proc; |
| }; |
| ¶ |
| PFN_vkVoidFunction Lookup(const char* name, const NameProc* begin, const NameProc* end) { |
| const auto& entry = std::lower_bound( |
| begin, end, name, |
| [](const NameProc& e, const char* n) { return strcmp(e.name, n) < 0; }); |
| if (entry == end || strcmp(entry->name, name) != 0) |
| return nullptr; |
| return entry->proc; |
| } |
| ¶ |
| template <size_t N> |
| PFN_vkVoidFunction Lookup(const char* name, const NameProc (&procs)[N]) { |
| return Lookup(name, procs, procs + N); |
| } |
| ¶ |
| const NameProc kLoaderExportProcs[] = {« |
| // clang-format off |
| {{range $f := SortBy (AllCommands $) "FunctionName"}} |
| {{if (Macro "IsExported" $f)}} |
| {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>({{$f.Name}})}, |
| {{end}} |
| {{end}} |
| // clang-format on |
| »}; |
| ¶ |
| const NameProc kLoaderGlobalProcs[] = {« |
| // clang-format off |
| {{range $f := SortBy (AllCommands $) "FunctionName"}} |
| {{if and (Macro "HasLoaderTopImpl" $f) (eq (Macro "Vtbl" $f) "Global")}} |
| {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§ |
| static_cast<{{Macro "FunctionPtrName" $f}}>(§ |
| {{Macro "BaseName" $f}}_Top))}, |
| {{end}} |
| {{end}} |
| // clang-format on |
| »}; |
| ¶ |
| const NameProc kLoaderTopProcs[] = {« |
| // clang-format off |
| {{range $f := SortBy (AllCommands $) "FunctionName"}} |
| {{if (Macro "HasLoaderTopImpl" $f)}} |
| {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§ |
| static_cast<{{Macro "FunctionPtrName" $f}}>(§ |
| {{Macro "BaseName" $f}}_Top))}, |
| {{end}} |
| {{end}} |
| // clang-format on |
| »}; |
| ¶ |
| const NameProc kLoaderBottomProcs[] = {« |
| // clang-format off |
| {{range $f := SortBy (AllCommands $) "FunctionName"}} |
| {{if (Macro "HasLoaderBottomImpl" $f)}} |
| {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§ |
| static_cast<{{Macro "FunctionPtrName" $f}}>(§ |
| {{Macro "BaseName" $f}}_Bottom))}, |
| {{end}} |
| {{end}} |
| // clang-format on |
| »}; |
| ¶ |
| struct NameOffset { |
| const char* name; |
| size_t offset; |
| }; |
| ¶ |
| ssize_t Lookup(const char* name, |
| const NameOffset* begin, |
| const NameOffset* end) { |
| const auto& entry = std::lower_bound( |
| begin, end, name, [](const NameOffset& e, const char* n) { |
| return strcmp(e.name, n) < 0; |
| }); |
| if (entry == end || strcmp(entry->name, name) != 0) |
| return -1; |
| return static_cast<ssize_t>(entry->offset); |
| } |
| ¶ |
| template <size_t N, class Table> |
| PFN_vkVoidFunction Lookup(const char* name, |
| const NameOffset (&offsets)[N], |
| const Table& table) { |
| ssize_t offset = Lookup(name, offsets, offsets + N); |
| if (offset < 0) |
| return nullptr; |
| uintptr_t base = reinterpret_cast<uintptr_t>(&table); |
| return *reinterpret_cast<PFN_vkVoidFunction*>(base + |
| static_cast<size_t>(offset)); |
| } |
| ¶ |
| const NameOffset kInstanceDispatchOffsets[] = {« |
| // clang-format off |
| {{range $f := SortBy (AllCommands $) "FunctionName"}} |
| {{if (Macro "IsInstanceDispatched" $f)}} |
| {"{{$f.Name}}", offsetof(InstanceDispatchTable, {{Macro "BaseName" $f}})}, |
| {{end}} |
| {{end}} |
| // clang-format on |
| »}; |
| ¶ |
| const NameOffset kDeviceDispatchOffsets[] = {« |
| // clang-format off |
| {{range $f := SortBy (AllCommands $) "FunctionName"}} |
| {{if (Macro "IsDeviceDispatched" $f)}} |
| {"{{$f.Name}}", offsetof(DeviceDispatchTable, {{Macro "BaseName" $f}})}, |
| {{end}} |
| {{end}} |
| // clang-format on |
| »}; |
| ¶ |
| } // anonymous namespace |
| ¶ |
| namespace vulkan { |
| ¶ |
| PFN_vkVoidFunction GetLoaderExportProcAddr(const char* name) { |
| return Lookup(name, kLoaderExportProcs); |
| } |
| ¶ |
| PFN_vkVoidFunction GetLoaderGlobalProcAddr(const char* name) { |
| return Lookup(name, kLoaderGlobalProcs); |
| } |
| ¶ |
| PFN_vkVoidFunction GetLoaderTopProcAddr(const char* name) { |
| return Lookup(name, kLoaderTopProcs); |
| } |
| ¶ |
| PFN_vkVoidFunction GetLoaderBottomProcAddr(const char* name) { |
| return Lookup(name, kLoaderBottomProcs); |
| } |
| ¶ |
| PFN_vkVoidFunction GetDispatchProcAddr(const InstanceDispatchTable& dispatch, |
| const char* name) { |
| return Lookup(name, kInstanceDispatchOffsets, dispatch); |
| } |
| ¶ |
| PFN_vkVoidFunction GetDispatchProcAddr(const DeviceDispatchTable& dispatch, |
| const char* name) { |
| return Lookup(name, kDeviceDispatchOffsets, dispatch); |
| } |
| ¶ |
| bool LoadInstanceDispatchTable(VkInstance instance, |
| PFN_vkGetInstanceProcAddr get_proc_addr, |
| InstanceDispatchTable& dispatch) {« |
| bool success = true; |
| // clang-format off |
| {{range $f := AllCommands $}} |
| {{if (Macro "IsInstanceDispatched" $f)}} |
| dispatch.{{Macro "BaseName" $f}} = § |
| reinterpret_cast<{{Macro "FunctionPtrName" $f}}>(§ |
| get_proc_addr(instance, "{{$f.Name}}")); |
| if (UNLIKELY(!dispatch.{{Macro "BaseName" $f}})) { |
| ALOGE("missing instance proc: %s", "{{$f.Name}}"); |
| success = false; |
| } |
| {{end}} |
| {{end}} |
| // clang-format on |
| return success; |
| »} |
| ¶ |
| bool LoadDeviceDispatchTable(VkDevice device, |
| PFN_vkGetDeviceProcAddr get_proc_addr, |
| DeviceDispatchTable& dispatch) {« |
| bool success = true; |
| // clang-format off |
| {{range $f := AllCommands $}} |
| {{if (Macro "IsDeviceDispatched" $f)}} |
| dispatch.{{Macro "BaseName" $f}} = § |
| reinterpret_cast<{{Macro "FunctionPtrName" $f}}>(§ |
| get_proc_addr(device, "{{$f.Name}}")); |
| if (UNLIKELY(!dispatch.{{Macro "BaseName" $f}})) { |
| ALOGE("missing device proc: %s", "{{$f.Name}}"); |
| success = false; |
| } |
| {{end}} |
| {{end}} |
| // clang-format on |
| return success; |
| »} |
| ¶ |
| bool LoadDriverDispatchTable(VkInstance instance, |
| PFN_vkGetInstanceProcAddr get_proc_addr, |
| const InstanceExtensionSet& extensions, |
| DriverDispatchTable& dispatch) {« |
| bool success = true; |
| // clang-format off |
| {{range $f := AllCommands $}} |
| {{if (Macro "IsInstanceDispatched" $f)}} |
| {{if not (Macro "IsLoaderFunction" $f)}} |
| {{$ext := GetAnnotation $f "extension"}} |
| {{if $ext}} |
| if (extensions[{{Macro "ExtensionConstant" $ext}}]) { |
| {{end}} |
| dispatch.{{Macro "BaseName" $f}} = § |
| reinterpret_cast<{{Macro "FunctionPtrName" $f}}>(§ |
| get_proc_addr(instance, "{{$f.Name}}")); |
| if (UNLIKELY(!dispatch.{{Macro "BaseName" $f}})) { |
| ALOGE("missing driver proc: %s", "{{$f.Name}}"); |
| success = false; |
| } |
| {{if $ext}} |
| } |
| {{end}} |
| {{end}} |
| {{end}} |
| {{end}} |
| dispatch.GetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(get_proc_addr(instance, "vkGetDeviceProcAddr")); |
| if (UNLIKELY(!dispatch.GetDeviceProcAddr)) { |
| ALOGE("missing driver proc: %s", "vkGetDeviceProcAddr"); |
| success = false; |
| } |
| dispatch.CreateImage = reinterpret_cast<PFN_vkCreateImage>(get_proc_addr(instance, "vkCreateImage")); |
| if (UNLIKELY(!dispatch.CreateImage)) { |
| ALOGE("missing driver proc: %s", "vkCreateImage"); |
| success = false; |
| } |
| dispatch.DestroyImage = reinterpret_cast<PFN_vkDestroyImage>(get_proc_addr(instance, "vkDestroyImage")); |
| if (UNLIKELY(!dispatch.DestroyImage)) { |
| ALOGE("missing driver proc: %s", "vkDestroyImage"); |
| success = false; |
| } |
| dispatch.GetSwapchainGrallocUsageANDROID = reinterpret_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(get_proc_addr(instance, "vkGetSwapchainGrallocUsageANDROID")); |
| if (UNLIKELY(!dispatch.GetSwapchainGrallocUsageANDROID)) { |
| ALOGE("missing driver proc: %s", "vkGetSwapchainGrallocUsageANDROID"); |
| success = false; |
| } |
| dispatch.AcquireImageANDROID = reinterpret_cast<PFN_vkAcquireImageANDROID>(get_proc_addr(instance, "vkAcquireImageANDROID")); |
| if (UNLIKELY(!dispatch.AcquireImageANDROID)) { |
| ALOGE("missing driver proc: %s", "vkAcquireImageANDROID"); |
| success = false; |
| } |
| dispatch.QueueSignalReleaseImageANDROID = reinterpret_cast<PFN_vkQueueSignalReleaseImageANDROID>(get_proc_addr(instance, "vkQueueSignalReleaseImageANDROID")); |
| if (UNLIKELY(!dispatch.QueueSignalReleaseImageANDROID)) { |
| ALOGE("missing driver proc: %s", "vkQueueSignalReleaseImageANDROID"); |
| success = false; |
| } |
| // clang-format on |
| return success; |
| »} |
| ¶ |
| } // namespace vulkan |
| ¶ |
| // clang-format off |
| ¶ |
| {{range $f := AllCommands $}} |
| {{if and (not (GetAnnotation $f "pfn")) (Macro "IsExported" $f)}} |
| __attribute__((visibility("default"))) |
| VKAPI_ATTR {{Node "Type" $f.Return}} {{$f.Name}}({{Macro "Parameters" $f}}) { |
| {{if not (IsVoid $f.Return.Type)}}return §{{end}} |
| {{Macro "Dispatch" $f}}({{Macro "Arguments" $f}}); |
| } |
| ¶ |
| {{end}} |
| {{end}} |
| ¶ |
| // clang-format on |
| ¶{{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------- |
| Emit the dispatch lookup for a function based on its first parameter. |
| ------------------------------------------------------------------------------- |
| */}} |
| {{define "Dispatch"}} |
| {{AssertType $ "Function"}} |
| |
| {{if (Macro "HasLoaderTopImpl" $)}} |
| {{Macro "BaseName" $}}_Top§ |
| {{else}} |
| {{$p0 := index $.CallParameters 0}} |
| GetDispatchTable({{$p0.Name}}).{{Macro "BaseName" $}}§ |
| {{end}} |
| {{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------- |
| Map an extension name to InstanceExtension or DeviceExtension enum value |
| ------------------------------------------------------------------------------- |
| */}} |
| {{define "ExtensionConstant"}} |
| {{$name := index $.Arguments 0}} |
| {{ if (eq $name "VK_KHR_surface")}}kKHR_surface |
| {{else if (eq $name "VK_KHR_android_surface")}}kKHR_android_surface |
| {{else if (eq $name "VK_EXT_debug_report")}}kEXT_debug_report |
| {{end}} |
| {{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------- |
| Emits a function name without the "vk" prefix. |
| ------------------------------------------------------------------------------- |
| */}} |
| {{define "BaseName"}} |
| {{AssertType $ "Function"}} |
| {{TrimPrefix "vk" $.Name}} |
| {{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------- |
| Emits a comma-separated list of C parameter names for the given command. |
| ------------------------------------------------------------------------------- |
| */}} |
| {{define "Arguments"}} |
| {{AssertType $ "Function"}} |
| |
| {{ForEach $.CallParameters "ParameterName" | JoinWith ", "}} |
| {{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------ |
| Emit "true" for supported functions that undergo table dispatch. Only global |
| functions and functions handled in the loader top without calling into |
| lower layers are not dispatched. |
| ------------------------------------------------------------------------------ |
| */}} |
| {{define "IsInstanceDispatched"}} |
| {{AssertType $ "Function"}} |
| {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Instance")}} |
| {{if (ne $.Name "vkGetInstanceProcAddr")}}true{{end}} |
| {{end}} |
| {{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------ |
| Emit "true" for supported functions that can have device-specific dispatch. |
| ------------------------------------------------------------------------------ |
| */}} |
| {{define "IsDeviceDispatched"}} |
| {{AssertType $ "Function"}} |
| {{if (Macro "IsFunctionSupported" $)}} |
| {{if eq (Macro "Vtbl" $) "Device"}} |
| {{if ne $.Name "vkGetDeviceProcAddr"}} |
| true |
| {{end}} |
| {{end}} |
| {{end}} |
| {{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------ |
| Emit "true" if a function is core or from a supportable extension. |
| ------------------------------------------------------------------------------ |
| */}} |
| {{define "IsFunctionSupported"}} |
| {{AssertType $ "Function"}} |
| {{if not (GetAnnotation $ "pfn")}} |
| {{$ext := GetAnnotation $ "extension"}} |
| {{if not $ext}}true |
| {{else if not (Macro "IsExtensionBlacklisted" $ext)}}true |
| {{end}} |
| {{end}} |
| {{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------ |
| Decides whether a function should be exported from the Android Vulkan |
| library. Functions in the core API and in loader extensions are exported. |
| ------------------------------------------------------------------------------ |
| */}} |
| {{define "IsExported"}} |
| {{AssertType $ "Function"}} |
| |
| {{if (Macro "IsFunctionSupported" $)}} |
| {{$ext := GetAnnotation $ "extension"}} |
| {{if $ext}} |
| {{Macro "IsLoaderExtension" $ext}} |
| {{else}} |
| true |
| {{end}} |
| {{end}} |
| {{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------ |
| Reports whether an extension function is implemented entirely by the loader, |
| and not implemented by drivers. |
| ------------------------------------------------------------------------------ |
| */}} |
| {{define "IsLoaderFunction"}} |
| {{AssertType $ "Function"}} |
| |
| {{$ext := GetAnnotation $ "extension"}} |
| {{if $ext}} |
| {{Macro "IsLoaderExtension" $ext}} |
| {{end}} |
| {{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------- |
| Emit "true" if the loader has a top-level implementation for the function |
| that should be called directly rather than dispatching to the first layer. |
| ------------------------------------------------------------------------------- |
| */}} |
| {{define "HasLoaderTopImpl"}} |
| {{AssertType $ "Function"}} |
| |
| {{/* Global functions can't be dispatched */}} |
| {{ if and (not (GetAnnotation $ "pfn")) (eq (Macro "Vtbl" $) "Global")}}true |
| |
| {{/* G*PA are implemented by reading the dispatch table, not by dispatching |
| through it. */}} |
| {{else if eq $.Name "vkGetInstanceProcAddr"}}true |
| {{else if eq $.Name "vkGetDeviceProcAddr"}}true |
| |
| {{/* Loader top needs to initialize dispatch for device-level dispatchable |
| objects */}} |
| {{else if eq $.Name "vkGetDeviceQueue"}}true |
| {{else if eq $.Name "vkAllocateCommandBuffers"}}true |
| |
| {{/* vkDestroy for dispatchable objects needs to handle VK_NULL_HANDLE; |
| trying to dispatch through that would crash. */}} |
| {{else if eq $.Name "vkDestroyInstance"}}true |
| {{else if eq $.Name "vkDestroyDevice"}}true |
| |
| {{end}} |
| {{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------- |
| Emit "true" if the loader has a bottom-level implementation for the function |
| which terminates the dispatch chain. |
| ------------------------------------------------------------------------------- |
| */}} |
| {{define "HasLoaderBottomImpl"}} |
| {{AssertType $ "Function"}} |
| |
| {{if (Macro "IsFunctionSupported" $)}} |
| {{ if (eq (Macro "Vtbl" $) "Instance")}}true |
| {{else if (Macro "IsLoaderFunction" $)}}true |
| {{else if (eq $.Name "vkCreateInstance")}}true |
| {{else if (eq $.Name "vkGetDeviceProcAddr")}}true |
| {{end}} |
| {{end}} |
| {{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------ |
| Emit "true" if an extension is unsupportable on Android. |
| ------------------------------------------------------------------------------ |
| */}} |
| {{define "IsExtensionBlacklisted"}} |
| {{$ext := index $.Arguments 0}} |
| {{ if eq $ext "VK_KHR_display"}}true |
| {{else if eq $ext "VK_KHR_display_swapchain"}}true |
| {{else if eq $ext "VK_KHR_xlib_surface"}}true |
| {{else if eq $ext "VK_KHR_xcb_surface"}}true |
| {{else if eq $ext "VK_KHR_wayland_surface"}}true |
| {{else if eq $ext "VK_KHR_mir_surface"}}true |
| {{else if eq $ext "VK_KHR_win32_surface"}}true |
| {{end}} |
| {{end}} |
| |
| |
| {{/* |
| ------------------------------------------------------------------------------ |
| Reports whether an extension is implemented entirely by the loader, |
| so drivers should not enumerate it. |
| ------------------------------------------------------------------------------ |
| */}} |
| {{define "IsLoaderExtension"}} |
| {{$ext := index $.Arguments 0}} |
| {{ if eq $ext "VK_KHR_surface"}}true |
| {{else if eq $ext "VK_KHR_swapchain"}}true |
| {{else if eq $ext "VK_KHR_android_surface"}}true |
| {{end}} |
| {{end}} |