vulkan: Implement new vkGet*ProcAddrBehavior
The primary goal of this change is to switch to the revised GPA
behavior:
- GIPA(NULL, ..) only works for non-dispatched (global) commands
- GIPA(instance, ..) returns functions for commands that dispatch on any
object type, and the function works for any object of the appropriate
type if it is a child of the instance.
- GDPA(NULL, ..) returns NULL.
- GDPA(device, ..) returns a device-specific function for the command.
This change refactors/tidies many of the things it modified. Some
notable changes:
- All the loader generated code is now in dispatch.tmpl ->
dispatch_gen.{h,cpp}, instead of two separate templates.
- Reorganization allowed generating the dispatch table structures,
eliminating one source of frequent bugs.
- Removes some error-prone macro duplication.
- Handling of extensions and special loader functions is now much
more uniform and hopefully clearer.
- Loader top- and bottom-level functions are now consistently named with
_Top and _Bottom suffixes, and are grouped by level in loader.cpp.
- The VkInstance and VkDevice implementations are no longer derived from
::VkInstance_T and ::VkDevice_T. Was more trouble than it was worth.
- Renamed 'vtbl' to 'dispatch' in most places.
- Renamed nulldrv template and generated files to match the loader
naming pattern: null_driver.tmpl -> null_driver_gen.{h,cpp}
- Now all the entry point prototypes are generated, instead of having
to be updated by hand (another source of several bugs).
Change-Id: Ic263f802d0d523b18a0f00420b3a722aa04ce299
(cherry picked from commit 3cffb8e837222f413a1fe53522e2cc33366b8eeb)
diff --git a/vulkan/libvulkan/dispatch.tmpl b/vulkan/libvulkan/dispatch.tmpl
new file mode 100644
index 0000000..a387d81
--- /dev/null
+++ b/vulkan/libvulkan/dispatch.tmpl
@@ -0,0 +1,589 @@
+{{/*
+ * 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_PROTOTYPES
+#define VK_USE_PLATFORM_ANDROID_KHR
+#include <vulkan/vk_android_native_buffer.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 "IsFunctionSupported" $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,
+ DriverDispatchTable& dispatch) {«
+ bool success = true;
+ // clang-format off
+ {{range $f := AllCommands $}}
+ {{if (Macro "IsInstanceDispatched" $f)}}
+ {{if not (Macro "IsLoaderFunction" $f)}}
+ 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;
+ }
+ {{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.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}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+ 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"}}
+
+ {{$ext := GetAnnotation $ "extension"}}
+ {{if $ext}}
+ {{Macro "IsLoaderExtension" $ext}}
+ {{else}}
+ true
+ {{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}}