blob: 8ea8eeb24afd82c44046c49f811c1d9199e6a03e [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/fdio/fdio.h>
#include <lib/zx/channel.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vulkan/vk_icd.h>
#include <vulkan/vulkan.h>
// This file contains a fake Vulkan ICD that implements everything up to and including
// vkCreateInstance.
VKAPI_ATTR __attribute__((visibility("default"))) VkResult VKAPI_CALL
vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pVersion) {
fprintf(stderr, "Got icd Negotiate loader ICD interface version\n");
*pVersion = 3;
return VK_SUCCESS;
}
namespace {
bool open_in_namespace_callback_initialized = false;
struct Instance {
Instance() : loader_magic(ICD_LOADER_MAGIC) {}
// Instance is a dispatchable object, and the loader uses the first 8 bytes as a pointer. See
// https://github.com/KhronosGroup/Vulkan-Loader/blob/master/loader/LoaderAndLayerInterface.md#icd-dispatchable-object-creation
uintptr_t loader_magic;
};
VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkInstance* pInstance) {
// Check that the open in namespace proc was set and was valid.
if (!open_in_namespace_callback_initialized)
return VK_ERROR_INITIALIZATION_FAILED;
*pInstance = reinterpret_cast<VkInstance>(new Instance);
return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProperties(
const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties) {
*pPropertyCount = 0;
return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL vkDestroyInstance(VkInstance instance,
const VkAllocationCallbacks* pAllocator) {
delete reinterpret_cast<Instance*>(instance);
}
VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceVersion(uint32_t* pApiVersion) {
*pApiVersion = VK_API_VERSION_1_0;
return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures* pFeatures) {}
VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties(
VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties* pFormatProperties) {}
VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties(
VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling,
VkImageUsageFlags usage, VkImageCreateFlags flags,
VkImageFormatProperties* pImageFormatProperties) {
return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceProperties* pProperties) {}
VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties(
VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount,
VkQueueFamilyProperties* pQueueFamilyProperties) {}
VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties(
VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties) {}
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice device, const char* pName) {
return nullptr;
}
VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice,
const VkDeviceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDevice* pDevice) {
return VK_ERROR_INITIALIZATION_FAILED;
}
// Only the bare minimum Vulkan 1.0 core entrypoints are implemented. These are just enough for
// vkCreateInstance to succeed.
#define DEF_FUNCTION(name) \
{ #name, reinterpret_cast < PFN_vkVoidFunction>(name) }
struct FunctionTable {
std::string name;
PFN_vkVoidFunction function;
} functions[] = {
DEF_FUNCTION(vkCreateInstance),
DEF_FUNCTION(vkDestroyInstance),
DEF_FUNCTION(vkEnumerateInstanceVersion),
DEF_FUNCTION(vkEnumerateInstanceExtensionProperties),
DEF_FUNCTION(vkEnumeratePhysicalDevices),
DEF_FUNCTION(vkGetPhysicalDeviceFeatures),
DEF_FUNCTION(vkGetPhysicalDeviceFormatProperties),
DEF_FUNCTION(vkGetPhysicalDeviceImageFormatProperties),
DEF_FUNCTION(vkGetPhysicalDeviceProperties),
DEF_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties),
DEF_FUNCTION(vkGetPhysicalDeviceMemoryProperties),
DEF_FUNCTION(vkGetDeviceProcAddr),
DEF_FUNCTION(vkCreateDevice),
DEF_FUNCTION(vkEnumerateDeviceExtensionProperties),
DEF_FUNCTION(vkGetPhysicalDeviceSparseImageFormatProperties),
};
#undef DEF_FUNCTION
} // namespace
VKAPI_ATTR __attribute__((visibility("default"))) PFN_vkVoidFunction VKAPI_CALL
vk_icdGetInstanceProcAddr(VkInstance instance, const char* pName) {
for (auto& function : functions) {
if (function.name == pName)
return function.function;
}
return nullptr;
}
VKAPI_ATTR __attribute__((visibility("default"))) PFN_vkVoidFunction VKAPI_CALL
vk_icdGetPhysicalDeviceProcAddr(VkInstance instance, const char* pName) {
return nullptr;
}
extern "C" {
typedef VkResult(VKAPI_PTR* PFN_vkOpenInNamespaceAddr)(const char* pName, uint32_t handle);
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
vk_icdInitializeOpenInNamespaceCallback(PFN_vkOpenInNamespaceAddr addr);
}
VKAPI_ATTR __attribute__((visibility("default"))) PFN_vkVoidFunction VKAPI_CALL
vk_icdInitializeOpenInNamespaceCallback(PFN_vkOpenInNamespaceAddr open_in_namespace_addr) {
zx::channel server_end, client_end;
zx::channel::create(0, &server_end, &client_end);
// A hermetic ICD shouldn't try to access to anything from the loader service.
if (!getenv("HERMETIC_ICD")) {
// ConnectToDeviceFs in the service provider should connect the device fs to /pkg/data.
VkResult result =
open_in_namespace_addr("/loader-gpu-devices/libvulkan_fake.json", server_end.release());
if (result != VK_SUCCESS) {
fprintf(stderr, "Opening libvulkan_fake.json failed with error %d\n", result);
return nullptr;
}
fdio_t* fdio;
zx_status_t status = fdio_create(client_end.release(), &fdio);
if (status != ZX_OK) {
fprintf(stderr, "fdio create failed with status %d\n", status);
return nullptr;
}
int fd = fdio_bind_to_fd(fdio, -1, 0);
if (fd < 0) {
fprintf(stderr, "fdio_bind_to_fd failed\n");
return nullptr;
}
}
open_in_namespace_callback_initialized = true;
return nullptr;
}