Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1f91d390 authored by Jesse Hall's avatar Jesse Hall
Browse files

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)
parent 21597661
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -216,7 +216,8 @@
{{define "Vtbl"}}
  {{AssertType $ "Function"}}

  {{range $i, $p := $.CallParameters}}
    {{if not $i}}{{Node "Vtbl" $p}}{{end}}
  {{if gt (len $.CallParameters) 0}}
    {{Node "Vtbl" (index $.CallParameters 0)}}
  {{else}}Global
  {{end}}
{{end}}
+1 −2
Original line number Diff line number Diff line
@@ -30,8 +30,7 @@ LOCAL_C_INCLUDES := \
	system/core/libsync/include

LOCAL_SRC_FILES := \
	entry.cpp \
	get_proc_addr.cpp \
	dispatch_gen.cpp \
	loader.cpp \
	swapchain.cpp \
	vulkan_loader_data.cpp
+589 −0
Original line number Diff line number Diff line
{{/*
 * 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}}
+2037 −0

File added.

Preview size limit exceeded, changes collapsed.

+200 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading