Skip to content

[Vulkan] Add support for MoltenVK on macOS #256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/MoltenVK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Enabling Vulkan on macOS with MoltenVK

To enable Vulkan testing on macOS you first need to download and install the
Vulkan SDK from [here](https://vulkan.lunarg.com).

By default the SDK installs into your home directory under
`~/VulkanSDK/${SDK_Version}/macOS`. For CMake to find the SDK you either need to
set the `VULKAN_SDK` environment variable to the macOS subdirectory of the
VulkanSDK installation you wish to use. You also need to run `sudo
~/VulkanSDK/${SDK_Version}/install_vulkan.py --force-install`, to install the
development binaries into `/usr/local/...` so that launched applications can
find them.

Once the SDK is installed and exposed to CMake, a clean configuration will
detect Vulkan and the MoltenVK portability layer and enable the Vulkan test
configurations.
72 changes: 61 additions & 11 deletions lib/API/VK/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ class VKDevice : public offloadtest::Device {
Capabilities Caps;
using LayerVector = std::vector<VkLayerProperties>;
LayerVector Layers;
using ExtensionVector = std::vector<VkExtensionProperties>;
ExtensionVector Extensions;

struct BufferRef {
VkBuffer Buffer;
Expand Down Expand Up @@ -226,6 +228,20 @@ class VKDevice : public offloadtest::Device {
return false;
}

const ExtensionVector &getExtensions() {
if (Extensions.empty())
queryExtensions();
return Extensions;
}

bool isExtensionSupported(llvm::StringRef QueryName) {
for (const auto &Ext : getExtensions()) {
if (Ext.extensionName == QueryName)
return true;
}
return false;
}

void printExtra(llvm::raw_ostream &OS) override {
OS << " Layers:\n";
for (auto Layer : getLayers()) {
Expand All @@ -236,6 +252,12 @@ class VKDevice : public offloadtest::Device {
Sz = strnlen(Layer.description, VK_MAX_DESCRIPTION_SIZE);
OS << " LayerDesc: " << llvm::StringRef(Layer.description, Sz) << "\n";
}

OS << " Extensions:\n";
for (const auto &Ext : getExtensions()) {
OS << " - ExtensionName: " << llvm::StringRef(Ext.extensionName) << "\n";
OS << " SpecVersion: " << Ext.specVersion << "\n";
}
}

const VkPhysicalDeviceProperties &getProps() const { return Props; }
Expand Down Expand Up @@ -301,6 +323,19 @@ class VKDevice : public offloadtest::Device {
vkEnumerateInstanceLayerProperties(&LayerCount, Layers.data());
}

void queryExtensions() {
assert(Extensions.empty() && "Should not be called twice!");
uint32_t ExtCount;
vkEnumerateDeviceExtensionProperties(Device, nullptr, &ExtCount, nullptr);

if (ExtCount == 0)
return;

Extensions.insert(Extensions.begin(), ExtCount, VkExtensionProperties());
vkEnumerateDeviceExtensionProperties(Device, nullptr, &ExtCount,
Extensions.data());
}

public:
llvm::Error createDevice(InvocationState &IS) {

Expand Down Expand Up @@ -906,10 +941,20 @@ class VKContext {
CreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
CreateInfo.pApplicationInfo = &AppInfo;

llvm::SmallVector<const char *> Extensions;
llvm::SmallVector<const char *> Layers;
#if __APPLE__
Extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
CreateInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
#endif

CreateInfo.ppEnabledExtensionNames = Extensions.data();
CreateInfo.enabledExtensionCount = Extensions.size();

VkResult Res = vkCreateInstance(&CreateInfo, NULL, &Instance);
if (Res == VK_ERROR_INCOMPATIBLE_DRIVER)
return llvm::createStringError(std::errc::no_such_device,
"Cannot find a compatible Vulkan device");
"Cannot find a base Vulkan device");
if (Res)
return llvm::createStringError(std::errc::no_such_device,
"Unknown Vulkan initialization error: %d",
Expand All @@ -927,21 +972,26 @@ class VKContext {
{
auto TmpDev = std::make_shared<VKDevice>(PhysicalDevicesTmp[0]);
AppInfo.apiVersion = TmpDev->getProps().apiVersion;
}
vkDestroyInstance(Instance, NULL);
Instance = VK_NULL_HANDLE;

// TODO: This is a bit hacky but matches what I did in DX.
#ifndef NDEBUG
const char *ValidationLayer = "VK_LAYER_KHRONOS_validation";
CreateInfo.ppEnabledLayerNames = &ValidationLayer;
CreateInfo.enabledLayerCount = 1;
const llvm::StringRef ValidationLayer = "VK_LAYER_KHRONOS_validation";
if (TmpDev->isLayerSupported(ValidationLayer))
Layers.push_back(ValidationLayer.data());

const char *DebugUtilsExtensionName = "VK_EXT_debug_utils";
CreateInfo.ppEnabledExtensionNames = &DebugUtilsExtensionName;
CreateInfo.enabledExtensionCount = 1;
const llvm::StringRef DebugUtilsExtensionName = "VK_EXT_debug_utils";
if (TmpDev->isExtensionSupported(DebugUtilsExtensionName))
Extensions.push_back(DebugUtilsExtensionName.data());
#endif
CreateInfo.ppEnabledLayerNames = Layers.data();
CreateInfo.enabledLayerCount = Layers.size();
CreateInfo.ppEnabledExtensionNames = Extensions.data();
CreateInfo.enabledExtensionCount = Extensions.size();
}
vkDestroyInstance(Instance, NULL);
Instance = VK_NULL_HANDLE;

// This second creation shouldn't ever fail, but it tries to create the
// highest supported device version.
Res = vkCreateInstance(&CreateInfo, NULL, &Instance);
if (Res == VK_ERROR_INCOMPATIBLE_DRIVER)
return llvm::createStringError(std::errc::no_such_device,
Expand Down
7 changes: 5 additions & 2 deletions test/Feature/HLSLLib/sign.32.test
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ Buffers:
- Name: Out2
Format: Int32
Stride: 16
ZeroInitSize: 48
ZeroInitSize: 48
- Name: ExpectedOut2 # The result we expect
Format: Int32
Stride: 16
Data: [1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0] # Last two are filler
- Name: Out3
Format: Int32
Stride: 16
ZeroInitSize: 48
ZeroInitSize: 48
- Name: ExpectedOut3 # The result we expect
Format: Int32
Stride: 16
Expand Down Expand Up @@ -176,6 +176,9 @@ DescriptorSets:
# https://github.com/microsoft/DirectXShaderCompiler/issues/7512
# XFAIL: DXC-Vulkan

# No idea what is going on here, but Out1 and Out3 are coming through as 0's.
# XFAIL: Clang && Vulkan-Darwin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Particle life is probably harder, but this one feel like we should have a follow up task to figure out what is going wrong.


# RUN: split-file %s %t
# RUN: %dxc_target -T cs_6_5 -Fo %t.o %t/source.hlsl
# RUN: %offloader %t/pipeline.yaml %t.o
3 changes: 3 additions & 0 deletions test/UseCase/particle-life.test
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,9 @@ DescriptorSets:
# https://github.com/llvm-beanz/offload-test-suite/issues/55
# UNSUPPORTED: Metal

# No idea what is going on here, but the results are _way_ off.
# XFAIL: Vulkan-Darwin

# RUN: split-file %s %t
# RUN: %if !Vulkan %{ %dxc_target -T cs_6_0 -Fo %t.o %t/particle-life.hlsl %}
# RUN: %if Vulkan %{ %dxc_target -T cs_6_0 -fspv-target-env=vulkan1.3 -fvk-use-scalar-layout -Fo %t.o %t/particle-life.hlsl %}
Expand Down
1 change: 1 addition & 0 deletions test/lit.cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
def setDeviceFeatures(config, device, compiler):
API = device["API"]
config.available_features.add(API)
config.available_features.add("%s-%s" % (API, config.offloadtest_os))
if "Microsoft Basic Render Driver" in device["Description"]:
config.available_features.add("%s-WARP" % API)
if "Intel" in device["Description"]:
Expand Down
1 change: 1 addition & 0 deletions test/lit.site.cfg.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ config.offloadtest_suite = "@suite@"
config.offloadtest_enable_d3d12 = @TEST_d3d12@
config.offloadtest_enable_vulkan = @TEST_vk@
config.offloadtest_enable_metal = @TEST_mtl@
config.offloadtest_os = "@CMAKE_SYSTEM_NAME@"

import lit.llvm
lit.llvm.initialize(lit_config, config)
Expand Down
6 changes: 6 additions & 0 deletions tools/api-query/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ add_offloadtest_tool(api-query
api-query.cpp)

target_link_libraries(api-query PRIVATE LLVMSupport OffloadTestAPI)

if (APPLE AND OFFLOADTEST_ENABLE_VULKAN)
get_filename_component(_Vulkan_LIB_DIR ${Vulkan_LIBRARY} DIRECTORY)
set_property(TARGET api-query APPEND_STRING PROPERTY
LINK_FLAGS " -Wl,-rpath,${_Vulkan_LIB_DIR} ")
endif()
6 changes: 6 additions & 0 deletions tools/offloader/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ target_link_libraries(offloader PRIVATE
OffloadTestAPI
OffloadTestImage
OffloadTestSupport)

if (APPLE AND OFFLOADTEST_ENABLE_VULKAN)
get_filename_component(_Vulkan_LIB_DIR ${Vulkan_LIBRARY} DIRECTORY)
set_property(TARGET offloader APPEND_STRING PROPERTY
LINK_FLAGS " -Wl,-rpath,${_Vulkan_LIB_DIR} ")
endif()
Loading