1086 lines
35 KiB
C++
1086 lines
35 KiB
C++
/*
|
|
* Copyright 2016 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 <malloc.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/prctl.h>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <dlfcn.h>
|
|
#include <new>
|
|
|
|
#include <log/log.h>
|
|
|
|
#include <android/dlext.h>
|
|
#include <cutils/properties.h>
|
|
#include <graphicsenv/GraphicsEnv.h>
|
|
#include <utils/Vector.h>
|
|
|
|
#include "android-base/properties.h"
|
|
|
|
#include "driver.h"
|
|
#include "stubhal.h"
|
|
|
|
// TODO(b/37049319) Get this from a header once one exists
|
|
extern "C" {
|
|
android_namespace_t* android_get_exported_namespace(const char*);
|
|
}
|
|
|
|
// #define ENABLE_ALLOC_CALLSTACKS 1
|
|
#if ENABLE_ALLOC_CALLSTACKS
|
|
#include <utils/CallStack.h>
|
|
#define ALOGD_CALLSTACK(...) \
|
|
do { \
|
|
ALOGD(__VA_ARGS__); \
|
|
android::CallStack callstack; \
|
|
callstack.update(); \
|
|
callstack.log(LOG_TAG, ANDROID_LOG_DEBUG, " "); \
|
|
} while (false)
|
|
#else
|
|
#define ALOGD_CALLSTACK(...) \
|
|
do { \
|
|
} while (false)
|
|
#endif
|
|
|
|
namespace vulkan {
|
|
namespace driver {
|
|
|
|
namespace {
|
|
|
|
class Hal {
|
|
public:
|
|
static bool Open();
|
|
|
|
static const Hal& Get() { return hal_; }
|
|
static const hwvulkan_device_t& Device() { return *Get().dev_; }
|
|
|
|
int GetDebugReportIndex() const { return debug_report_index_; }
|
|
|
|
private:
|
|
Hal() : dev_(nullptr), debug_report_index_(-1) {}
|
|
Hal(const Hal&) = delete;
|
|
Hal& operator=(const Hal&) = delete;
|
|
|
|
bool InitDebugReportIndex();
|
|
|
|
static Hal hal_;
|
|
|
|
const hwvulkan_device_t* dev_;
|
|
int debug_report_index_;
|
|
};
|
|
|
|
class CreateInfoWrapper {
|
|
public:
|
|
CreateInfoWrapper(const VkInstanceCreateInfo& create_info,
|
|
const VkAllocationCallbacks& allocator);
|
|
CreateInfoWrapper(VkPhysicalDevice physical_dev,
|
|
const VkDeviceCreateInfo& create_info,
|
|
const VkAllocationCallbacks& allocator);
|
|
~CreateInfoWrapper();
|
|
|
|
VkResult Validate();
|
|
|
|
const std::bitset<ProcHook::EXTENSION_COUNT>& GetHookExtensions() const;
|
|
const std::bitset<ProcHook::EXTENSION_COUNT>& GetHalExtensions() const;
|
|
|
|
explicit operator const VkInstanceCreateInfo*() const;
|
|
explicit operator const VkDeviceCreateInfo*() const;
|
|
|
|
private:
|
|
struct ExtensionFilter {
|
|
VkExtensionProperties* exts;
|
|
uint32_t ext_count;
|
|
|
|
const char** names;
|
|
uint32_t name_count;
|
|
};
|
|
|
|
VkResult SanitizePNext();
|
|
|
|
VkResult SanitizeLayers();
|
|
VkResult SanitizeExtensions();
|
|
|
|
VkResult QueryExtensionCount(uint32_t& count) const;
|
|
VkResult EnumerateExtensions(uint32_t& count,
|
|
VkExtensionProperties* props) const;
|
|
VkResult InitExtensionFilter();
|
|
void FilterExtension(const char* name);
|
|
|
|
const bool is_instance_;
|
|
const VkAllocationCallbacks& allocator_;
|
|
|
|
VkPhysicalDevice physical_dev_;
|
|
|
|
union {
|
|
VkInstanceCreateInfo instance_info_;
|
|
VkDeviceCreateInfo dev_info_;
|
|
};
|
|
|
|
ExtensionFilter extension_filter_;
|
|
|
|
std::bitset<ProcHook::EXTENSION_COUNT> hook_extensions_;
|
|
std::bitset<ProcHook::EXTENSION_COUNT> hal_extensions_;
|
|
};
|
|
|
|
Hal Hal::hal_;
|
|
|
|
void* LoadLibrary(const android_dlextinfo& dlextinfo,
|
|
const char* subname,
|
|
int subname_len) {
|
|
const char kLibFormat[] = "vulkan.%*s.so";
|
|
char* name = static_cast<char*>(
|
|
alloca(sizeof(kLibFormat) + static_cast<size_t>(subname_len)));
|
|
sprintf(name, kLibFormat, subname_len, subname);
|
|
return android_dlopen_ext(name, RTLD_LOCAL | RTLD_NOW, &dlextinfo);
|
|
}
|
|
|
|
const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{
|
|
"ro.hardware." HWVULKAN_HARDWARE_MODULE_ID,
|
|
"ro.board.platform",
|
|
}};
|
|
|
|
int LoadDriver(android_namespace_t* library_namespace,
|
|
const hwvulkan_module_t** module) {
|
|
const android_dlextinfo dlextinfo = {
|
|
.flags = ANDROID_DLEXT_USE_NAMESPACE,
|
|
.library_namespace = library_namespace,
|
|
};
|
|
void* so = nullptr;
|
|
char prop[PROPERTY_VALUE_MAX];
|
|
for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
|
|
int prop_len = property_get(key, prop, nullptr);
|
|
if (prop_len > 0) {
|
|
so = LoadLibrary(dlextinfo, prop, prop_len);
|
|
if (so)
|
|
break;
|
|
}
|
|
}
|
|
if (!so)
|
|
return -ENOENT;
|
|
|
|
auto hmi = static_cast<hw_module_t*>(dlsym(so, HAL_MODULE_INFO_SYM_AS_STR));
|
|
if (!hmi) {
|
|
ALOGE("couldn't find symbol '%s' in HAL library: %s", HAL_MODULE_INFO_SYM_AS_STR, dlerror());
|
|
dlclose(so);
|
|
return -EINVAL;
|
|
}
|
|
if (strcmp(hmi->id, HWVULKAN_HARDWARE_MODULE_ID) != 0) {
|
|
ALOGE("HAL id '%s' != '%s'", hmi->id, HWVULKAN_HARDWARE_MODULE_ID);
|
|
dlclose(so);
|
|
return -EINVAL;
|
|
}
|
|
hmi->dso = so;
|
|
*module = reinterpret_cast<const hwvulkan_module_t*>(hmi);
|
|
return 0;
|
|
}
|
|
|
|
int LoadBuiltinDriver(const hwvulkan_module_t** module) {
|
|
auto ns = android_get_exported_namespace("sphal");
|
|
if (!ns)
|
|
return -ENOENT;
|
|
return LoadDriver(ns, module);
|
|
}
|
|
|
|
int LoadUpdatedDriver(const hwvulkan_module_t** module) {
|
|
auto ns = android::GraphicsEnv::getInstance().getDriverNamespace();
|
|
if (!ns)
|
|
return -ENOENT;
|
|
return LoadDriver(ns, module);
|
|
}
|
|
|
|
bool Hal::Open() {
|
|
ALOG_ASSERT(!hal_.dev_, "OpenHAL called more than once");
|
|
|
|
// Use a stub device unless we successfully open a real HAL device.
|
|
hal_.dev_ = &stubhal::kDevice;
|
|
|
|
int result;
|
|
const hwvulkan_module_t* module = nullptr;
|
|
|
|
result = LoadUpdatedDriver(&module);
|
|
if (result == -ENOENT) {
|
|
result = LoadBuiltinDriver(&module);
|
|
if (result != 0) {
|
|
// -ENOENT means the sphal namespace doesn't exist, not that there
|
|
// is a problem with the driver.
|
|
ALOGW_IF(
|
|
result != -ENOENT,
|
|
"Failed to load Vulkan driver into sphal namespace. This "
|
|
"usually means the driver has forbidden library dependencies."
|
|
"Please fix, this will soon stop working.");
|
|
result =
|
|
hw_get_module(HWVULKAN_HARDWARE_MODULE_ID,
|
|
reinterpret_cast<const hw_module_t**>(&module));
|
|
}
|
|
}
|
|
if (result != 0) {
|
|
ALOGV("unable to load Vulkan HAL, using stub HAL (result=%d)", result);
|
|
return true;
|
|
}
|
|
|
|
hwvulkan_device_t* device;
|
|
result =
|
|
module->common.methods->open(&module->common, HWVULKAN_DEVICE_0,
|
|
reinterpret_cast<hw_device_t**>(&device));
|
|
if (result != 0) {
|
|
// Any device with a Vulkan HAL should be able to open the device.
|
|
ALOGE("failed to open Vulkan HAL device: %s (%d)", strerror(-result),
|
|
result);
|
|
return false;
|
|
}
|
|
|
|
hal_.dev_ = device;
|
|
|
|
hal_.InitDebugReportIndex();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Hal::InitDebugReportIndex() {
|
|
uint32_t count;
|
|
if (dev_->EnumerateInstanceExtensionProperties(nullptr, &count, nullptr) !=
|
|
VK_SUCCESS) {
|
|
ALOGE("failed to get HAL instance extension count");
|
|
return false;
|
|
}
|
|
|
|
VkExtensionProperties* exts = reinterpret_cast<VkExtensionProperties*>(
|
|
malloc(sizeof(VkExtensionProperties) * count));
|
|
if (!exts) {
|
|
ALOGE("failed to allocate HAL instance extension array");
|
|
return false;
|
|
}
|
|
|
|
if (dev_->EnumerateInstanceExtensionProperties(nullptr, &count, exts) !=
|
|
VK_SUCCESS) {
|
|
ALOGE("failed to enumerate HAL instance extensions");
|
|
free(exts);
|
|
return false;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
if (strcmp(exts[i].extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME) ==
|
|
0) {
|
|
debug_report_index_ = static_cast<int>(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(exts);
|
|
|
|
return true;
|
|
}
|
|
|
|
CreateInfoWrapper::CreateInfoWrapper(const VkInstanceCreateInfo& create_info,
|
|
const VkAllocationCallbacks& allocator)
|
|
: is_instance_(true),
|
|
allocator_(allocator),
|
|
physical_dev_(VK_NULL_HANDLE),
|
|
instance_info_(create_info),
|
|
extension_filter_() {
|
|
hook_extensions_.set(ProcHook::EXTENSION_CORE);
|
|
hal_extensions_.set(ProcHook::EXTENSION_CORE);
|
|
}
|
|
|
|
CreateInfoWrapper::CreateInfoWrapper(VkPhysicalDevice physical_dev,
|
|
const VkDeviceCreateInfo& create_info,
|
|
const VkAllocationCallbacks& allocator)
|
|
: is_instance_(false),
|
|
allocator_(allocator),
|
|
physical_dev_(physical_dev),
|
|
dev_info_(create_info),
|
|
extension_filter_() {
|
|
hook_extensions_.set(ProcHook::EXTENSION_CORE);
|
|
hal_extensions_.set(ProcHook::EXTENSION_CORE);
|
|
}
|
|
|
|
CreateInfoWrapper::~CreateInfoWrapper() {
|
|
allocator_.pfnFree(allocator_.pUserData, extension_filter_.exts);
|
|
allocator_.pfnFree(allocator_.pUserData, extension_filter_.names);
|
|
}
|
|
|
|
VkResult CreateInfoWrapper::Validate() {
|
|
VkResult result = SanitizePNext();
|
|
if (result == VK_SUCCESS)
|
|
result = SanitizeLayers();
|
|
if (result == VK_SUCCESS)
|
|
result = SanitizeExtensions();
|
|
|
|
return result;
|
|
}
|
|
|
|
const std::bitset<ProcHook::EXTENSION_COUNT>&
|
|
CreateInfoWrapper::GetHookExtensions() const {
|
|
return hook_extensions_;
|
|
}
|
|
|
|
const std::bitset<ProcHook::EXTENSION_COUNT>&
|
|
CreateInfoWrapper::GetHalExtensions() const {
|
|
return hal_extensions_;
|
|
}
|
|
|
|
CreateInfoWrapper::operator const VkInstanceCreateInfo*() const {
|
|
return &instance_info_;
|
|
}
|
|
|
|
CreateInfoWrapper::operator const VkDeviceCreateInfo*() const {
|
|
return &dev_info_;
|
|
}
|
|
|
|
VkResult CreateInfoWrapper::SanitizePNext() {
|
|
const struct StructHeader {
|
|
VkStructureType type;
|
|
const void* next;
|
|
} * header;
|
|
|
|
if (is_instance_) {
|
|
header = reinterpret_cast<const StructHeader*>(instance_info_.pNext);
|
|
|
|
// skip leading VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFOs
|
|
while (header &&
|
|
header->type == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO)
|
|
header = reinterpret_cast<const StructHeader*>(header->next);
|
|
|
|
instance_info_.pNext = header;
|
|
} else {
|
|
header = reinterpret_cast<const StructHeader*>(dev_info_.pNext);
|
|
|
|
// skip leading VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFOs
|
|
while (header &&
|
|
header->type == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO)
|
|
header = reinterpret_cast<const StructHeader*>(header->next);
|
|
|
|
dev_info_.pNext = header;
|
|
}
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult CreateInfoWrapper::SanitizeLayers() {
|
|
auto& layer_names = (is_instance_) ? instance_info_.ppEnabledLayerNames
|
|
: dev_info_.ppEnabledLayerNames;
|
|
auto& layer_count = (is_instance_) ? instance_info_.enabledLayerCount
|
|
: dev_info_.enabledLayerCount;
|
|
|
|
// remove all layers
|
|
layer_names = nullptr;
|
|
layer_count = 0;
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult CreateInfoWrapper::SanitizeExtensions() {
|
|
auto& ext_names = (is_instance_) ? instance_info_.ppEnabledExtensionNames
|
|
: dev_info_.ppEnabledExtensionNames;
|
|
auto& ext_count = (is_instance_) ? instance_info_.enabledExtensionCount
|
|
: dev_info_.enabledExtensionCount;
|
|
if (!ext_count)
|
|
return VK_SUCCESS;
|
|
|
|
VkResult result = InitExtensionFilter();
|
|
if (result != VK_SUCCESS)
|
|
return result;
|
|
|
|
for (uint32_t i = 0; i < ext_count; i++)
|
|
FilterExtension(ext_names[i]);
|
|
|
|
ext_names = extension_filter_.names;
|
|
ext_count = extension_filter_.name_count;
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult CreateInfoWrapper::QueryExtensionCount(uint32_t& count) const {
|
|
if (is_instance_) {
|
|
return Hal::Device().EnumerateInstanceExtensionProperties(
|
|
nullptr, &count, nullptr);
|
|
} else {
|
|
const auto& driver = GetData(physical_dev_).driver;
|
|
return driver.EnumerateDeviceExtensionProperties(physical_dev_, nullptr,
|
|
&count, nullptr);
|
|
}
|
|
}
|
|
|
|
VkResult CreateInfoWrapper::EnumerateExtensions(
|
|
uint32_t& count,
|
|
VkExtensionProperties* props) const {
|
|
if (is_instance_) {
|
|
return Hal::Device().EnumerateInstanceExtensionProperties(
|
|
nullptr, &count, props);
|
|
} else {
|
|
const auto& driver = GetData(physical_dev_).driver;
|
|
return driver.EnumerateDeviceExtensionProperties(physical_dev_, nullptr,
|
|
&count, props);
|
|
}
|
|
}
|
|
|
|
VkResult CreateInfoWrapper::InitExtensionFilter() {
|
|
// query extension count
|
|
uint32_t count;
|
|
VkResult result = QueryExtensionCount(count);
|
|
if (result != VK_SUCCESS || count == 0)
|
|
return result;
|
|
|
|
auto& filter = extension_filter_;
|
|
filter.exts =
|
|
reinterpret_cast<VkExtensionProperties*>(allocator_.pfnAllocation(
|
|
allocator_.pUserData, sizeof(VkExtensionProperties) * count,
|
|
alignof(VkExtensionProperties),
|
|
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
|
|
if (!filter.exts)
|
|
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
|
|
// enumerate extensions
|
|
result = EnumerateExtensions(count, filter.exts);
|
|
if (result != VK_SUCCESS && result != VK_INCOMPLETE)
|
|
return result;
|
|
|
|
if (!count)
|
|
return VK_SUCCESS;
|
|
|
|
filter.ext_count = count;
|
|
|
|
// allocate name array
|
|
uint32_t enabled_ext_count = (is_instance_)
|
|
? instance_info_.enabledExtensionCount
|
|
: dev_info_.enabledExtensionCount;
|
|
count = std::min(filter.ext_count, enabled_ext_count);
|
|
filter.names = reinterpret_cast<const char**>(allocator_.pfnAllocation(
|
|
allocator_.pUserData, sizeof(const char*) * count, alignof(const char*),
|
|
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
|
|
if (!filter.names)
|
|
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
void CreateInfoWrapper::FilterExtension(const char* name) {
|
|
auto& filter = extension_filter_;
|
|
|
|
ProcHook::Extension ext_bit = GetProcHookExtension(name);
|
|
if (is_instance_) {
|
|
switch (ext_bit) {
|
|
case ProcHook::KHR_android_surface:
|
|
case ProcHook::KHR_surface:
|
|
case ProcHook::EXT_swapchain_colorspace:
|
|
case ProcHook::KHR_get_surface_capabilities2:
|
|
hook_extensions_.set(ext_bit);
|
|
// return now as these extensions do not require HAL support
|
|
return;
|
|
case ProcHook::EXT_debug_report:
|
|
// both we and HAL can take part in
|
|
hook_extensions_.set(ext_bit);
|
|
break;
|
|
case ProcHook::EXTENSION_UNKNOWN:
|
|
case ProcHook::KHR_get_physical_device_properties2:
|
|
// HAL's extensions
|
|
break;
|
|
default:
|
|
ALOGW("Ignored invalid instance extension %s", name);
|
|
return;
|
|
}
|
|
} else {
|
|
switch (ext_bit) {
|
|
case ProcHook::KHR_swapchain:
|
|
// map VK_KHR_swapchain to VK_ANDROID_native_buffer
|
|
name = VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME;
|
|
ext_bit = ProcHook::ANDROID_native_buffer;
|
|
break;
|
|
case ProcHook::KHR_incremental_present:
|
|
case ProcHook::GOOGLE_display_timing:
|
|
case ProcHook::KHR_shared_presentable_image:
|
|
hook_extensions_.set(ext_bit);
|
|
// return now as these extensions do not require HAL support
|
|
return;
|
|
case ProcHook::EXT_hdr_metadata:
|
|
hook_extensions_.set(ext_bit);
|
|
break;
|
|
case ProcHook::EXTENSION_UNKNOWN:
|
|
// HAL's extensions
|
|
break;
|
|
default:
|
|
ALOGW("Ignored invalid device extension %s", name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (uint32_t i = 0; i < filter.ext_count; i++) {
|
|
const VkExtensionProperties& props = filter.exts[i];
|
|
// ignore unknown extensions
|
|
if (strcmp(name, props.extensionName) != 0)
|
|
continue;
|
|
|
|
filter.names[filter.name_count++] = name;
|
|
if (ext_bit != ProcHook::EXTENSION_UNKNOWN) {
|
|
if (ext_bit == ProcHook::ANDROID_native_buffer)
|
|
hook_extensions_.set(ProcHook::KHR_swapchain);
|
|
|
|
hal_extensions_.set(ext_bit);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
VKAPI_ATTR void* DefaultAllocate(void*,
|
|
size_t size,
|
|
size_t alignment,
|
|
VkSystemAllocationScope) {
|
|
void* ptr = nullptr;
|
|
// Vulkan requires 'alignment' to be a power of two, but posix_memalign
|
|
// additionally requires that it be at least sizeof(void*).
|
|
int ret = posix_memalign(&ptr, std::max(alignment, sizeof(void*)), size);
|
|
ALOGD_CALLSTACK("Allocate: size=%zu align=%zu => (%d) %p", size, alignment,
|
|
ret, ptr);
|
|
return ret == 0 ? ptr : nullptr;
|
|
}
|
|
|
|
VKAPI_ATTR void* DefaultReallocate(void*,
|
|
void* ptr,
|
|
size_t size,
|
|
size_t alignment,
|
|
VkSystemAllocationScope) {
|
|
if (size == 0) {
|
|
free(ptr);
|
|
return nullptr;
|
|
}
|
|
|
|
// TODO(jessehall): Right now we never shrink allocations; if the new
|
|
// request is smaller than the existing chunk, we just continue using it.
|
|
// Right now the loader never reallocs, so this doesn't matter. If that
|
|
// changes, or if this code is copied into some other project, this should
|
|
// probably have a heuristic to allocate-copy-free when doing so will save
|
|
// "enough" space.
|
|
size_t old_size = ptr ? malloc_usable_size(ptr) : 0;
|
|
if (size <= old_size)
|
|
return ptr;
|
|
|
|
void* new_ptr = nullptr;
|
|
if (posix_memalign(&new_ptr, std::max(alignment, sizeof(void*)), size) != 0)
|
|
return nullptr;
|
|
if (ptr) {
|
|
memcpy(new_ptr, ptr, std::min(old_size, size));
|
|
free(ptr);
|
|
}
|
|
return new_ptr;
|
|
}
|
|
|
|
VKAPI_ATTR void DefaultFree(void*, void* ptr) {
|
|
ALOGD_CALLSTACK("Free: %p", ptr);
|
|
free(ptr);
|
|
}
|
|
|
|
InstanceData* AllocateInstanceData(const VkAllocationCallbacks& allocator) {
|
|
void* data_mem = allocator.pfnAllocation(
|
|
allocator.pUserData, sizeof(InstanceData), alignof(InstanceData),
|
|
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
|
|
if (!data_mem)
|
|
return nullptr;
|
|
|
|
return new (data_mem) InstanceData(allocator);
|
|
}
|
|
|
|
void FreeInstanceData(InstanceData* data,
|
|
const VkAllocationCallbacks& allocator) {
|
|
data->~InstanceData();
|
|
allocator.pfnFree(allocator.pUserData, data);
|
|
}
|
|
|
|
DeviceData* AllocateDeviceData(
|
|
const VkAllocationCallbacks& allocator,
|
|
const DebugReportCallbackList& debug_report_callbacks) {
|
|
void* data_mem = allocator.pfnAllocation(
|
|
allocator.pUserData, sizeof(DeviceData), alignof(DeviceData),
|
|
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
|
|
if (!data_mem)
|
|
return nullptr;
|
|
|
|
return new (data_mem) DeviceData(allocator, debug_report_callbacks);
|
|
}
|
|
|
|
void FreeDeviceData(DeviceData* data, const VkAllocationCallbacks& allocator) {
|
|
data->~DeviceData();
|
|
allocator.pfnFree(allocator.pUserData, data);
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
bool Debuggable() {
|
|
return (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) >= 0);
|
|
}
|
|
|
|
bool OpenHAL() {
|
|
return Hal::Open();
|
|
}
|
|
|
|
const VkAllocationCallbacks& GetDefaultAllocator() {
|
|
static const VkAllocationCallbacks kDefaultAllocCallbacks = {
|
|
.pUserData = nullptr,
|
|
.pfnAllocation = DefaultAllocate,
|
|
.pfnReallocation = DefaultReallocate,
|
|
.pfnFree = DefaultFree,
|
|
};
|
|
|
|
return kDefaultAllocCallbacks;
|
|
}
|
|
|
|
PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName) {
|
|
const ProcHook* hook = GetProcHook(pName);
|
|
if (!hook)
|
|
return Hal::Device().GetInstanceProcAddr(instance, pName);
|
|
|
|
if (!instance) {
|
|
if (hook->type == ProcHook::GLOBAL)
|
|
return hook->proc;
|
|
|
|
// v0 layers expect
|
|
//
|
|
// vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateDevice");
|
|
//
|
|
// to work.
|
|
if (strcmp(pName, "vkCreateDevice") == 0)
|
|
return hook->proc;
|
|
|
|
ALOGE(
|
|
"internal vkGetInstanceProcAddr called for %s without an instance",
|
|
pName);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
PFN_vkVoidFunction proc;
|
|
|
|
switch (hook->type) {
|
|
case ProcHook::INSTANCE:
|
|
proc = (GetData(instance).hook_extensions[hook->extension])
|
|
? hook->proc
|
|
: nullptr;
|
|
break;
|
|
case ProcHook::DEVICE:
|
|
proc = (hook->extension == ProcHook::EXTENSION_CORE)
|
|
? hook->proc
|
|
: hook->checked_proc;
|
|
break;
|
|
default:
|
|
ALOGE(
|
|
"internal vkGetInstanceProcAddr called for %s with an instance",
|
|
pName);
|
|
proc = nullptr;
|
|
break;
|
|
}
|
|
|
|
return proc;
|
|
}
|
|
|
|
PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pName) {
|
|
const ProcHook* hook = GetProcHook(pName);
|
|
if (!hook)
|
|
return GetData(device).driver.GetDeviceProcAddr(device, pName);
|
|
|
|
if (hook->type != ProcHook::DEVICE) {
|
|
ALOGE("internal vkGetDeviceProcAddr called for %s", pName);
|
|
return nullptr;
|
|
}
|
|
|
|
return (GetData(device).hook_extensions[hook->extension]) ? hook->proc
|
|
: nullptr;
|
|
}
|
|
|
|
VkResult EnumerateInstanceExtensionProperties(
|
|
const char* pLayerName,
|
|
uint32_t* pPropertyCount,
|
|
VkExtensionProperties* pProperties) {
|
|
|
|
android::Vector<VkExtensionProperties> loader_extensions;
|
|
loader_extensions.push_back({
|
|
VK_KHR_SURFACE_EXTENSION_NAME,
|
|
VK_KHR_SURFACE_SPEC_VERSION});
|
|
loader_extensions.push_back({
|
|
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
|
|
VK_KHR_ANDROID_SURFACE_SPEC_VERSION});
|
|
loader_extensions.push_back({
|
|
VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME,
|
|
VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION});
|
|
loader_extensions.push_back({
|
|
VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,
|
|
VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION});
|
|
|
|
static const VkExtensionProperties loader_debug_report_extension = {
|
|
VK_EXT_DEBUG_REPORT_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_SPEC_VERSION,
|
|
};
|
|
|
|
// enumerate our extensions first
|
|
if (!pLayerName && pProperties) {
|
|
uint32_t count = std::min(
|
|
*pPropertyCount, static_cast<uint32_t>(loader_extensions.size()));
|
|
|
|
std::copy_n(loader_extensions.begin(), count, pProperties);
|
|
|
|
if (count < loader_extensions.size()) {
|
|
*pPropertyCount = count;
|
|
return VK_INCOMPLETE;
|
|
}
|
|
|
|
pProperties += count;
|
|
*pPropertyCount -= count;
|
|
|
|
if (Hal::Get().GetDebugReportIndex() < 0) {
|
|
if (!*pPropertyCount) {
|
|
*pPropertyCount = count;
|
|
return VK_INCOMPLETE;
|
|
}
|
|
|
|
pProperties[0] = loader_debug_report_extension;
|
|
pProperties += 1;
|
|
*pPropertyCount -= 1;
|
|
}
|
|
}
|
|
|
|
VkResult result = Hal::Device().EnumerateInstanceExtensionProperties(
|
|
pLayerName, pPropertyCount, pProperties);
|
|
|
|
if (!pLayerName && (result == VK_SUCCESS || result == VK_INCOMPLETE)) {
|
|
int idx = Hal::Get().GetDebugReportIndex();
|
|
if (idx < 0) {
|
|
*pPropertyCount += 1;
|
|
} else if (pProperties &&
|
|
static_cast<uint32_t>(idx) < *pPropertyCount) {
|
|
pProperties[idx].specVersion =
|
|
std::min(pProperties[idx].specVersion,
|
|
loader_debug_report_extension.specVersion);
|
|
}
|
|
|
|
*pPropertyCount += loader_extensions.size();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool QueryPresentationProperties(
|
|
VkPhysicalDevice physicalDevice,
|
|
VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties)
|
|
{
|
|
const InstanceData& data = GetData(physicalDevice);
|
|
|
|
// GPDP2 must be present and enabled on the instance.
|
|
if (!data.driver.GetPhysicalDeviceProperties2KHR)
|
|
return false;
|
|
|
|
// Request the android-specific presentation properties via GPDP2
|
|
VkPhysicalDeviceProperties2KHR properties = {
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
|
|
presentation_properties,
|
|
{}
|
|
};
|
|
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wold-style-cast"
|
|
presentation_properties->sType =
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID;
|
|
#pragma clang diagnostic pop
|
|
presentation_properties->pNext = nullptr;
|
|
presentation_properties->sharedImage = VK_FALSE;
|
|
|
|
data.driver.GetPhysicalDeviceProperties2KHR(physicalDevice,
|
|
&properties);
|
|
|
|
return true;
|
|
}
|
|
|
|
VkResult EnumerateDeviceExtensionProperties(
|
|
VkPhysicalDevice physicalDevice,
|
|
const char* pLayerName,
|
|
uint32_t* pPropertyCount,
|
|
VkExtensionProperties* pProperties) {
|
|
const InstanceData& data = GetData(physicalDevice);
|
|
// extensions that are unconditionally exposed by the loader
|
|
android::Vector<VkExtensionProperties> loader_extensions;
|
|
loader_extensions.push_back({
|
|
VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME,
|
|
VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION});
|
|
|
|
VkPhysicalDevicePresentationPropertiesANDROID presentation_properties;
|
|
if (QueryPresentationProperties(physicalDevice, &presentation_properties) &&
|
|
presentation_properties.sharedImage) {
|
|
loader_extensions.push_back({
|
|
VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME,
|
|
VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION});
|
|
}
|
|
|
|
// conditionally add VK_GOOGLE_display_timing if present timestamps are
|
|
// supported by the driver:
|
|
const std::string timestamp_property("service.sf.present_timestamp");
|
|
android::base::WaitForPropertyCreation(timestamp_property);
|
|
if (android::base::GetBoolProperty(timestamp_property, true)) {
|
|
loader_extensions.push_back({
|
|
VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME,
|
|
VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION});
|
|
}
|
|
|
|
// enumerate our extensions first
|
|
if (!pLayerName && pProperties) {
|
|
uint32_t count = std::min(
|
|
*pPropertyCount, static_cast<uint32_t>(loader_extensions.size()));
|
|
|
|
std::copy_n(loader_extensions.begin(), count, pProperties);
|
|
|
|
if (count < loader_extensions.size()) {
|
|
*pPropertyCount = count;
|
|
return VK_INCOMPLETE;
|
|
}
|
|
|
|
pProperties += count;
|
|
*pPropertyCount -= count;
|
|
}
|
|
|
|
VkResult result = data.driver.EnumerateDeviceExtensionProperties(
|
|
physicalDevice, pLayerName, pPropertyCount, pProperties);
|
|
|
|
if (pProperties) {
|
|
// map VK_ANDROID_native_buffer to VK_KHR_swapchain
|
|
for (uint32_t i = 0; i < *pPropertyCount; i++) {
|
|
auto& prop = pProperties[i];
|
|
|
|
if (strcmp(prop.extensionName,
|
|
VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0)
|
|
continue;
|
|
|
|
memcpy(prop.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
|
sizeof(VK_KHR_SWAPCHAIN_EXTENSION_NAME));
|
|
prop.specVersion = VK_KHR_SWAPCHAIN_SPEC_VERSION;
|
|
}
|
|
}
|
|
|
|
// restore loader extension count
|
|
if (!pLayerName && (result == VK_SUCCESS || result == VK_INCOMPLETE)) {
|
|
*pPropertyCount += loader_extensions.size();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
|
|
const VkAllocationCallbacks* pAllocator,
|
|
VkInstance* pInstance) {
|
|
const VkAllocationCallbacks& data_allocator =
|
|
(pAllocator) ? *pAllocator : GetDefaultAllocator();
|
|
|
|
if (pCreateInfo->pApplicationInfo &&
|
|
pCreateInfo->pApplicationInfo->apiVersion >= VK_MAKE_VERSION(1, 1, 0)) {
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wold-style-cast"
|
|
ALOGI(
|
|
"Requested Vulkan instance version %d.%d is greater than max "
|
|
"supported version (1.0)",
|
|
VK_VERSION_MAJOR(pCreateInfo->pApplicationInfo->apiVersion),
|
|
VK_VERSION_MINOR(pCreateInfo->pApplicationInfo->apiVersion));
|
|
#pragma clang diagnostic pop
|
|
return VK_ERROR_INCOMPATIBLE_DRIVER;
|
|
}
|
|
|
|
CreateInfoWrapper wrapper(*pCreateInfo, data_allocator);
|
|
VkResult result = wrapper.Validate();
|
|
if (result != VK_SUCCESS)
|
|
return result;
|
|
|
|
InstanceData* data = AllocateInstanceData(data_allocator);
|
|
if (!data)
|
|
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
|
|
data->hook_extensions |= wrapper.GetHookExtensions();
|
|
|
|
// call into the driver
|
|
VkInstance instance;
|
|
result = Hal::Device().CreateInstance(
|
|
static_cast<const VkInstanceCreateInfo*>(wrapper), pAllocator,
|
|
&instance);
|
|
if (result != VK_SUCCESS) {
|
|
FreeInstanceData(data, data_allocator);
|
|
return result;
|
|
}
|
|
|
|
// initialize InstanceDriverTable
|
|
if (!SetData(instance, *data) ||
|
|
!InitDriverTable(instance, Hal::Device().GetInstanceProcAddr,
|
|
wrapper.GetHalExtensions())) {
|
|
data->driver.DestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(
|
|
Hal::Device().GetInstanceProcAddr(instance, "vkDestroyInstance"));
|
|
if (data->driver.DestroyInstance)
|
|
data->driver.DestroyInstance(instance, pAllocator);
|
|
|
|
FreeInstanceData(data, data_allocator);
|
|
|
|
return VK_ERROR_INCOMPATIBLE_DRIVER;
|
|
}
|
|
|
|
data->get_device_proc_addr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(
|
|
Hal::Device().GetInstanceProcAddr(instance, "vkGetDeviceProcAddr"));
|
|
if (!data->get_device_proc_addr) {
|
|
data->driver.DestroyInstance(instance, pAllocator);
|
|
FreeInstanceData(data, data_allocator);
|
|
|
|
return VK_ERROR_INCOMPATIBLE_DRIVER;
|
|
}
|
|
|
|
*pInstance = instance;
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
void DestroyInstance(VkInstance instance,
|
|
const VkAllocationCallbacks* pAllocator) {
|
|
InstanceData& data = GetData(instance);
|
|
data.driver.DestroyInstance(instance, pAllocator);
|
|
|
|
VkAllocationCallbacks local_allocator;
|
|
if (!pAllocator) {
|
|
local_allocator = data.allocator;
|
|
pAllocator = &local_allocator;
|
|
}
|
|
|
|
FreeInstanceData(&data, *pAllocator);
|
|
}
|
|
|
|
VkResult CreateDevice(VkPhysicalDevice physicalDevice,
|
|
const VkDeviceCreateInfo* pCreateInfo,
|
|
const VkAllocationCallbacks* pAllocator,
|
|
VkDevice* pDevice) {
|
|
const InstanceData& instance_data = GetData(physicalDevice);
|
|
const VkAllocationCallbacks& data_allocator =
|
|
(pAllocator) ? *pAllocator : instance_data.allocator;
|
|
|
|
CreateInfoWrapper wrapper(physicalDevice, *pCreateInfo, data_allocator);
|
|
VkResult result = wrapper.Validate();
|
|
if (result != VK_SUCCESS)
|
|
return result;
|
|
|
|
DeviceData* data = AllocateDeviceData(data_allocator,
|
|
instance_data.debug_report_callbacks);
|
|
if (!data)
|
|
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
|
|
data->hook_extensions |= wrapper.GetHookExtensions();
|
|
|
|
// call into the driver
|
|
VkDevice dev;
|
|
result = instance_data.driver.CreateDevice(
|
|
physicalDevice, static_cast<const VkDeviceCreateInfo*>(wrapper),
|
|
pAllocator, &dev);
|
|
if (result != VK_SUCCESS) {
|
|
FreeDeviceData(data, data_allocator);
|
|
return result;
|
|
}
|
|
|
|
// initialize DeviceDriverTable
|
|
if (!SetData(dev, *data) ||
|
|
!InitDriverTable(dev, instance_data.get_device_proc_addr,
|
|
wrapper.GetHalExtensions())) {
|
|
data->driver.DestroyDevice = reinterpret_cast<PFN_vkDestroyDevice>(
|
|
instance_data.get_device_proc_addr(dev, "vkDestroyDevice"));
|
|
if (data->driver.DestroyDevice)
|
|
data->driver.DestroyDevice(dev, pAllocator);
|
|
|
|
FreeDeviceData(data, data_allocator);
|
|
|
|
return VK_ERROR_INCOMPATIBLE_DRIVER;
|
|
}
|
|
|
|
// sanity check ANDROID_native_buffer implementation, whose set of
|
|
// entrypoints varies according to the spec version.
|
|
if ((wrapper.GetHalExtensions()[ProcHook::ANDROID_native_buffer]) &&
|
|
!data->driver.GetSwapchainGrallocUsageANDROID &&
|
|
!data->driver.GetSwapchainGrallocUsage2ANDROID) {
|
|
ALOGE("Driver's implementation of ANDROID_native_buffer is broken;"
|
|
" must expose at least one of "
|
|
"vkGetSwapchainGrallocUsageANDROID or "
|
|
"vkGetSwapchainGrallocUsage2ANDROID");
|
|
|
|
data->driver.DestroyDevice(dev, pAllocator);
|
|
FreeDeviceData(data, data_allocator);
|
|
|
|
return VK_ERROR_INCOMPATIBLE_DRIVER;
|
|
}
|
|
|
|
VkPhysicalDeviceProperties properties;
|
|
instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
|
|
&properties);
|
|
|
|
data->driver_device = dev;
|
|
data->driver_version = properties.driverVersion;
|
|
|
|
*pDevice = dev;
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) {
|
|
DeviceData& data = GetData(device);
|
|
data.driver.DestroyDevice(device, pAllocator);
|
|
|
|
VkAllocationCallbacks local_allocator;
|
|
if (!pAllocator) {
|
|
local_allocator = data.allocator;
|
|
pAllocator = &local_allocator;
|
|
}
|
|
|
|
FreeDeviceData(&data, *pAllocator);
|
|
}
|
|
|
|
VkResult EnumeratePhysicalDevices(VkInstance instance,
|
|
uint32_t* pPhysicalDeviceCount,
|
|
VkPhysicalDevice* pPhysicalDevices) {
|
|
const auto& data = GetData(instance);
|
|
|
|
VkResult result = data.driver.EnumeratePhysicalDevices(
|
|
instance, pPhysicalDeviceCount, pPhysicalDevices);
|
|
if ((result == VK_SUCCESS || result == VK_INCOMPLETE) && pPhysicalDevices) {
|
|
for (uint32_t i = 0; i < *pPhysicalDeviceCount; i++)
|
|
SetData(pPhysicalDevices[i], data);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void GetDeviceQueue(VkDevice device,
|
|
uint32_t queueFamilyIndex,
|
|
uint32_t queueIndex,
|
|
VkQueue* pQueue) {
|
|
const auto& data = GetData(device);
|
|
|
|
data.driver.GetDeviceQueue(device, queueFamilyIndex, queueIndex, pQueue);
|
|
SetData(*pQueue, data);
|
|
}
|
|
|
|
VKAPI_ATTR VkResult
|
|
AllocateCommandBuffers(VkDevice device,
|
|
const VkCommandBufferAllocateInfo* pAllocateInfo,
|
|
VkCommandBuffer* pCommandBuffers) {
|
|
const auto& data = GetData(device);
|
|
|
|
VkResult result = data.driver.AllocateCommandBuffers(device, pAllocateInfo,
|
|
pCommandBuffers);
|
|
if (result == VK_SUCCESS) {
|
|
for (uint32_t i = 0; i < pAllocateInfo->commandBufferCount; i++)
|
|
SetData(pCommandBuffers[i], data);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
} // namespace driver
|
|
} // namespace vulkan
|