[PATCH vkd3d 3/3] Add support for MoltenVK on Mac OS.

Chip Davis cdavis at codeweavers.com
Thu Aug 2 11:53:54 CDT 2018


Both demo programs run flawlessly. Unfortunately, the d3d12 test is
broken, due to Metal getting an invalid pixel format. This needs
investigation and perhaps fixing on the MoltenVK side.

Signed-off-by: Chip Davis <cdavis at codeweavers.com>
---
 Makefile.am                            |   6 +-
 configure.ac                           |  21 +-
 demos/demo.h                           |   4 +
 demos/demo_mvk.h                       | 666 +++++++++++++++++++++++++
 libs/vkd3d-utils/vkd3d_utils_main.c    |   4 +
 libs/vkd3d-utils/vkd3d_utils_private.h |   5 +
 libs/vkd3d/device.c                    |  10 +-
 tests/vkd3d_api.c                      |  16 +
 8 files changed, 727 insertions(+), 5 deletions(-)
 create mode 100644 demos/demo_mvk.h

diff --git a/Makefile.am b/Makefile.am
index 4e8f942..fe87f44 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -47,6 +47,7 @@ vkd3d_demos = \
 vkd3d_demos_headers = \
 	demos/demo.h \
 	demos/demo_win32.h \
+	demos/demo_mvk.h \
 	demos/demo_xcb.h
 
 BUILT_SOURCES = $(widl_headers)
@@ -131,15 +132,18 @@ TESTS = $(vkd3d_tests) $(vkd3d_cross_tests)
 tests_d3d12_LDADD = $(LDADD) @PTHREAD_LIBS@
 tests_vkd3d_api_LDADD = libvkd3d.la @VULKAN_LIBS@
 
-DEMOS_LDADD = $(LDADD) libvkd3d-shader.la @XCB_LIBS@ @VULKAN_LIBS@
+DEMOS_LDADD = $(LDADD) libvkd3d-shader.la @XCB_LIBS@ @METAL_LIBS@ @VULKAN_LIBS@
 DEMOS_CFLAGS = $(AM_CFLAGS) @XCB_CFLAGS@
+DEMOS_CPPFLAGS = $(AM_CPPFLAGS) @METAL_CPPFLAGS@
 noinst_PROGRAMS += $(vkd3d_demos)
 EXTRA_DIST += $(vkd3d_test_headers) $(vkd3d_demos_headers)
 
 demos_gears_CFLAGS = $(DEMOS_CFLAGS)
+demos_gears_CPPFLAGS = $(DEMOS_CPPFLAGS)
 demos_gears_LDADD = $(DEMOS_LDADD) -lm
 
 demos_triangle_CFLAGS = $(DEMOS_CFLAGS)
+demos_triangle_CPPFLAGS = $(DEMOS_CPPFLAGS)
 demos_triangle_LDADD = $(DEMOS_LDADD)
 
 VKD3D_V_WIDL = $(vkd3d_v_widl_ at AM_V@)
diff --git a/configure.ac b/configure.ac
index 2773a15..c3f5a40 100644
--- a/configure.ac
+++ b/configure.ac
@@ -77,7 +77,9 @@ AC_CHECK_LIB([pthread], [pthread_create],
 AC_SUBST([VULKAN_LIBS])
 AC_CHECK_LIB([vulkan], [vkGetInstanceProcAddr],
              [VULKAN_LIBS="-lvulkan"],
-             [AC_MSG_ERROR([libvulkan not found.])])
+             [AC_CHECK_LIB([MoltenVK], [vkGetInstanceProcAddr],
+                           [VULKAN_LIBS="-lMoltenVK"],
+                           [AC_MSG_ERROR([libvulkan and libMoltenVK not found.])])])
 
 HAVE_SPIRV_TOOLS=no
 AS_IF([test "x$with_spirv_tools" = "xyes"],
@@ -85,7 +87,22 @@ AS_IF([test "x$with_spirv_tools" = "xyes"],
       [AC_DEFINE([HAVE_SPIRV_TOOLS], [1], [Define to 1 if you have SPIRV-Tools.])
       HAVE_SPIRV_TOOLS=yes])])
 
-PKG_CHECK_MODULES([XCB], [xcb xcb-keysyms])
+case $host_os in
+  darwin*|macosx*)
+    AC_LANG_PUSH([Objective C])
+    AC_CHECK_HEADERS(Metal/Metal.h MoltenVK/vk_mvk_moltenvk.h)
+    AC_LANG_POP([Objective C])
+    if test "x$ac_cv_header_Metal_Metal_h" = xno ||
+       test "x$ac_cv_header_MoltenVK_vk_mvk_moltenvk_h" = xno; then
+      AC_MSG_ERROR([Metal and MoltenVK required to use vkd3d on Mac OS.])
+    fi
+    AC_SUBST(METAL_LIBS, "-framework CoreFoundation -framework AppKit -framework QuartzCore -framework Metal")
+    AC_SUBST(METAL_CPPFLAGS, "-x objective-c")
+    ;;
+  *)
+    PKG_CHECK_MODULES([XCB], [xcb xcb-keysyms])
+    ;;
+esac
 
 dnl Check for functions
 VKD3D_CHECK_FUNC([HAVE_BUILTIN_CLZ], [__builtin_clz], [__builtin_clz(0)])
diff --git a/demos/demo.h b/demos/demo.h
index 2869ea7..de8eae0 100644
--- a/demos/demo.h
+++ b/demos/demo.h
@@ -143,5 +143,9 @@ static inline HRESULT demo_create_root_signature(ID3D12Device *device,
 #else
 #include <vkd3d_utils.h>
 #define INFINITE VKD3D_INFINITE
+#ifdef __APPLE__
+#include "demo_mvk.h"
+#else
 #include "demo_xcb.h"
 #endif
+#endif
diff --git a/demos/demo_mvk.h b/demos/demo_mvk.h
new file mode 100644
index 0000000..bcf5a4a
--- /dev/null
+++ b/demos/demo_mvk.h
@@ -0,0 +1,666 @@
+/*
+ * Copyright 2016 Józef Kucia for CodeWeavers
+ * Copyright 2016 Henri Verbeet for CodeWeavers
+ * Copyright 2018 Chip Davis for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define VK_USE_PLATFORM_MACOS_MVK
+#include <vkd3d.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdbool.h>
+
+#define BOOL OBJC_BOOL
+#undef interface
+#include <CoreFoundation/CFRunLoop.h>
+#import <Foundation/NSNotification.h>
+#import <Foundation/NSString.h>
+#import <AppKit/NSApplication.h>
+#import <AppKit/NSEvent.h>
+#import <AppKit/NSMenu.h>
+#import <AppKit/NSWindow.h>
+#import <AppKit/NSView.h>
+#import <QuartzCore/CAMetalLayer.h>
+#import <MoltenVK/vk_mvk_moltenvk.h>
+#undef BOOL
+
+#ifndef __MAC_OS_X_VERSION_10_12
+#define nonnull
+#endif
+
+ at class DemoView;
+
+struct demo
+{
+    struct demo_window **windows;
+    size_t windows_size;
+    size_t window_count;
+
+    void *user_data;
+    void (*idle_func)(struct demo *demo, void *user_data);
+};
+
+struct demo_window
+{
+    NSWindow *window;
+    DemoView *view;
+    struct demo *demo;
+
+    void *user_data;
+    void (*expose_func)(struct demo_window *window, void *user_data);
+    void (*key_press_func)(struct demo_window *window, demo_key key, void *user_data);
+};
+
+struct demo_swapchain
+{
+    VkSurfaceKHR vk_surface;
+    VkSwapchainKHR vk_swapchain;
+    VkFence vk_fence;
+
+    VkInstance vk_instance;
+    VkDevice vk_device;
+    ID3D12CommandQueue *command_queue;
+
+    uint32_t current_buffer;
+    unsigned int buffer_count;
+    ID3D12Resource *buffers[1];
+};
+
+static inline bool demo_add_window(struct demo *demo, struct demo_window *window)
+{
+    if (demo->window_count == demo->windows_size)
+    {
+        size_t new_capacity;
+        void *new_elements;
+
+        new_capacity = max(demo->windows_size * 2, 4);
+        if (!(new_elements = realloc(demo->windows, new_capacity * sizeof(*demo->windows))))
+            return false;
+        demo->windows = new_elements;
+        demo->windows_size = new_capacity;
+    }
+
+    demo->windows[demo->window_count++] = window;
+
+    return true;
+}
+
+static inline void demo_remove_window(struct demo *demo, const struct demo_window *window)
+{
+    size_t i;
+
+    for (i = 0; i < demo->window_count; ++i)
+    {
+        if (demo->windows[i] != window)
+            continue;
+
+        --demo->window_count;
+        memmove(&demo->windows[i], &demo->windows[i + 1], (demo->window_count - i) * sizeof(*demo->windows));
+        break;
+    }
+
+    if (!demo->window_count)
+        /* Time to stop. */
+        [NSApp stop:NSApp];
+}
+
+static inline struct demo_window *demo_find_window(struct demo *demo, NSWindow *window)
+{
+    size_t i;
+
+    for (i = 0; i < demo->window_count; ++i)
+    {
+        if (demo->windows[i]->window == window)
+            return demo->windows[i];
+    }
+
+    return NULL;
+}
+
+static inline struct demo_window *demo_window_create(struct demo *demo, const char *title,
+        unsigned int width, unsigned int height, void *user_data)
+{
+    struct demo_window *window;
+    NSAutoreleasePool *pool;
+
+    if (!(window = malloc(sizeof(*window))))
+        return NULL;
+
+    if (!demo_add_window(demo, window))
+    {
+        free(window);
+        return NULL;
+    }
+
+    pool = [[NSAutoreleasePool alloc] init];
+    window->window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, width, height)
+                                                 styleMask:NSTitledWindowMask|NSClosableWindowMask|
+                                                           NSMiniaturizableWindowMask
+                                                   backing:NSBackingStoreBuffered
+                                                     defer:YES];
+    window->view = nil;
+    window->demo = demo;
+    window->user_data = user_data;
+    window->expose_func = NULL;
+    window->key_press_func = NULL;
+    window->window.title = [NSString stringWithCString:title encoding:NSUTF8StringEncoding];
+    window->window.opaque = YES;
+    window->window.releasedWhenClosed = NO;
+
+    [window->window center];
+    [window->window makeKeyAndOrderFront:nil];
+
+    [pool release];
+
+    return window;
+}
+
+static inline void demo_window_destroy(struct demo_window *window)
+{
+    if (window->view)
+    {
+        [window->view release];
+        [NSApp removeWindowsItem:window->window];
+    }
+    [window->window release];
+    demo_remove_window(window->demo, window);
+    free(window);
+}
+
+static inline void demo_window_set_key_press_func(struct demo_window *window,
+        void (*key_press_func)(struct demo_window *window, demo_key key, void *user_data))
+{
+    window->key_press_func = key_press_func;
+}
+
+static inline void demo_window_set_expose_func(struct demo_window *window,
+        void (*expose_func)(struct demo_window *window, void *user_data))
+{
+    window->expose_func = expose_func;
+}
+
+static inline demo_key demo_key_from_nskey(unsigned short code)
+{
+    static const struct
+    {
+        unsigned short code;
+        demo_key demo_key;
+    }
+    lookup[] =
+    {
+        {0x35 /*kVK_Escape*/,     DEMO_KEY_ESCAPE},
+        {0x7B /*kVK_LeftArrow*/,  DEMO_KEY_LEFT},
+        {0x7C /*kVK_RightArrow*/, DEMO_KEY_RIGHT},
+        {0x7E /*kVK_UpArrow*/,    DEMO_KEY_UP},
+        {0x7D /*kVK_DownArrow*/,  DEMO_KEY_DOWN},
+    };
+    unsigned int i;
+
+    if (code >= '0' && code <= '9')
+        return code;
+    if (code >= 'A' && code <= 'Z')
+        return code;
+
+    for (i = 0; i < ARRAY_SIZE(lookup); ++i)
+    {
+        if (lookup[i].code == code)
+            return lookup[i].demo_key;
+    }
+
+    return DEMO_KEY_UNKNOWN;
+}
+
+ at interface DemoView : NSView <NSWindowDelegate>
+{
+    struct demo_window *_demo_window;
+    id<MTLDevice> _device;
+}
+
+- (instancetype)initWithWindow:(struct demo_window *)window device:(id<MTLDevice>)device;
+
+- (void)windowWillClose:(NSNotification *)notification;
+
+ at end
+
+ at implementation DemoView
+
+- (instancetype)initWithWindow:(struct demo_window *)window device:(id<MTLDevice>)device
+{
+    NSRect content_rect;
+
+    content_rect = [window->window contentRectForFrameRect:window->window.frame];
+    self = [super initWithFrame:content_rect];
+    if (self)
+    {
+        _demo_window = window;
+        _device = [device retain];
+        self.wantsLayer = YES;
+        self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever;
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    [_device release];
+    [super dealloc];
+}
+
+- (OBJC_BOOL)acceptsFirstResponder
+{
+    return YES;
+}
+
+- (void)keyDown:(NSEvent *)event
+{
+    demo_key sym;
+
+    [super keyDown:event];
+    if (!_demo_window->key_press_func)
+         return;
+    sym = demo_key_from_nskey(event.keyCode);
+    _demo_window->key_press_func(_demo_window, sym, _demo_window->user_data);
+}
+
+- (void)viewWillDraw
+{
+    if (_demo_window->expose_func)
+        _demo_window->expose_func(_demo_window, _demo_window->user_data);
+}
+
+- (CALayer *)makeBackingLayer
+{
+    CAMetalLayer *layer = [CAMetalLayer layer];
+    layer.device = _device;
+    layer.framebufferOnly = YES;
+    layer.magnificationFilter = kCAFilterNearest;
+    layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
+    return layer;
+}
+
+- (OBJC_BOOL)isOpaque
+{
+    return YES;
+}
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+    demo_window_destroy(_demo_window);
+}
+
+ at end
+
+static inline void demo_process_events(struct demo *demo)
+{
+    NSAutoreleasePool *pool;
+
+    if (demo->idle_func)
+    {
+        /* Stick a timer on the main run loop that will call the
+         * idle function every 1 ms. */
+        CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(NULL, 0.0, 0.001, 0, 0, ^(CFRunLoopTimerRef timer){
+            demo->idle_func(demo, demo->user_data);
+        });
+        CFRunLoopAddTimer(CFRunLoopGetMain(), timer, kCFRunLoopCommonModes);
+    }
+    pool = [[NSAutoreleasePool alloc] init];
+    [NSApp run];
+    [pool release];
+}
+
+static inline bool demo_init(struct demo *demo, void *user_data)
+{
+    NSMenu *mainMenu, *submenu;
+    NSMenuItem *item;
+
+    [NSApplication sharedApplication];
+    NSApp.activationPolicy = NSApplicationActivationPolicyRegular;
+
+    mainMenu = [[NSMenu alloc] init];
+    /* App menu */
+    submenu = [[NSMenu alloc] initWithTitle:@"VKD3D Demo"];
+    item = [submenu addItemWithTitle:@"Hide" action:@selector(hide:) keyEquivalent:@"h"];
+    item.keyEquivalentModifierMask = NSCommandKeyMask;
+    item = [submenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
+    item.keyEquivalentModifierMask = NSCommandKeyMask | NSAlternateKeyMask;
+    [submenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
+    [submenu addItem:[NSMenuItem separatorItem]];
+    item = [submenu addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
+    item.keyEquivalentModifierMask = NSCommandKeyMask;
+
+    item = [[NSMenuItem alloc] init];
+    item.title = @"VKD3D Demo";
+    item.submenu = submenu;
+    [mainMenu addItem:item];
+    [submenu release];
+    [item release];
+
+    /* Window menu */
+    submenu = [[NSMenu alloc] initWithTitle:@"Window"];
+    item = [submenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
+    item.keyEquivalentModifierMask = NSCommandKeyMask;
+    [submenu addItem:[NSMenuItem separatorItem]];
+    [submenu addItemWithTitle:@"Bring All to Front" action:@selector(arrangeInFront:) keyEquivalent:@""];
+
+    item = [[NSMenuItem alloc] init];
+    item.title = @"Window";
+    item.submenu = submenu;
+    [mainMenu addItem:item];
+    [item release];
+
+    NSApp.mainMenu = mainMenu;
+    NSApp.windowsMenu = submenu;
+    [submenu release];
+
+    demo->windows = NULL;
+    demo->windows_size = 0;
+    demo->window_count = 0;
+    demo->user_data = user_data;
+    demo->idle_func = NULL;
+
+    return true;
+}
+
+static inline void demo_cleanup(struct demo *demo)
+{
+    free(demo->windows);
+}
+
+static inline void demo_set_idle_func(struct demo *demo,
+        void (*idle_func)(struct demo *demo, void *user_data))
+{
+    demo->idle_func = idle_func;
+}
+
+static inline struct demo_swapchain *demo_swapchain_create(ID3D12CommandQueue *command_queue,
+        struct demo_window *window, const struct demo_swapchain_desc *desc)
+{
+    struct vkd3d_image_resource_create_info resource_create_info;
+    struct VkSwapchainCreateInfoKHR vk_swapchain_desc;
+    struct VkMacOSSurfaceCreateInfoMVK surface_desc;
+    VkSwapchainKHR vk_swapchain = VK_NULL_HANDLE;
+    uint32_t format_count, queue_family_index;
+    VkSurfaceCapabilitiesKHR surface_caps;
+    VkPhysicalDevice vk_physical_device;
+    VkFence vk_fence = VK_NULL_HANDLE;
+    struct demo_swapchain *swapchain;
+    unsigned int image_count, i, j;
+    VkFenceCreateInfo fence_desc;
+    VkSurfaceFormatKHR *formats;
+    ID3D12Device *d3d12_device;
+    VkSurfaceKHR vk_surface;
+    VkInstance vk_instance;
+    VkBool32 supported;
+    VkDevice vk_device;
+    VkImage *vk_images;
+    VkFormat format;
+    id<MTLDevice> mtl_device;
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    if ((format = vkd3d_get_vk_format(desc->format)) == VK_FORMAT_UNDEFINED)
+    {
+        [pool release];
+        return NULL;
+    }
+
+    if (FAILED(ID3D12CommandQueue_GetDevice(command_queue, &IID_ID3D12Device, (void **)&d3d12_device)))
+    {
+        [pool release];
+        return NULL;
+    }
+
+    vk_instance = vkd3d_instance_get_vk_instance(vkd3d_instance_from_device(d3d12_device));
+    vk_physical_device = vkd3d_get_vk_physical_device(d3d12_device);
+    vk_device = vkd3d_get_vk_device(d3d12_device);
+    vkGetMTLDeviceMVK(vk_physical_device, &mtl_device);
+
+    window->view = [[DemoView alloc] initWithWindow:window device:mtl_device];
+    window->window.initialFirstResponder = window->view;
+    window->window.contentView = window->view;
+    window->window.delegate = window->view;
+    [NSApp addWindowsItem:window->window title:window->window.title filename:NO];
+
+    surface_desc.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
+    surface_desc.pNext = NULL;
+    surface_desc.flags = 0;
+    surface_desc.pView = window->view;
+    if (vkCreateMacOSSurfaceMVK(vk_instance, &surface_desc, NULL, &vk_surface) < 0)
+    {
+        ID3D12Device_Release(d3d12_device);
+        [pool release];
+        return NULL;
+    }
+
+    queue_family_index = vkd3d_get_vk_queue_family_index(command_queue);
+    if (vkGetPhysicalDeviceSurfaceSupportKHR(vk_physical_device,
+            queue_family_index, vk_surface, &supported) < 0 || !supported)
+        goto fail;
+
+    if (vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_physical_device, vk_surface, &surface_caps) < 0)
+        goto fail;
+
+    if ((surface_caps.maxImageCount && desc->buffer_count > surface_caps.maxImageCount)
+            || desc->buffer_count < surface_caps.minImageCount
+            || desc->width > surface_caps.maxImageExtent.width || desc->width < surface_caps.minImageExtent.width
+            || desc->height > surface_caps.maxImageExtent.height || desc->height < surface_caps.minImageExtent.height
+            || !(surface_caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR))
+        goto fail;
+
+    if (vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physical_device, vk_surface, &format_count, NULL) < 0
+            || !format_count || !(formats = calloc(format_count, sizeof(*formats))))
+        goto fail;
+
+    if (vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physical_device, vk_surface, &format_count, formats) < 0)
+    {
+        free(formats);
+        goto fail;
+    }
+
+    if (format_count != 1 || formats->format != VK_FORMAT_UNDEFINED
+            || formats->colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
+    {
+        for (i = 0; i < format_count; ++i)
+        {
+            if (formats[i].format == format && formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
+                break;
+        }
+
+        if (i == format_count)
+        {
+            free(formats);
+            goto fail;
+        }
+    }
+
+    free(formats);
+    formats = NULL;
+
+    vk_swapchain_desc.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+    vk_swapchain_desc.pNext = NULL;
+    vk_swapchain_desc.flags = 0;
+    vk_swapchain_desc.surface = vk_surface;
+    vk_swapchain_desc.minImageCount = desc->buffer_count;
+    vk_swapchain_desc.imageFormat = format;
+    vk_swapchain_desc.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
+    vk_swapchain_desc.imageExtent.width = desc->width;
+    vk_swapchain_desc.imageExtent.height = desc->height;
+    vk_swapchain_desc.imageArrayLayers = 1;
+    vk_swapchain_desc.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+    vk_swapchain_desc.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    vk_swapchain_desc.queueFamilyIndexCount = 0;
+    vk_swapchain_desc.pQueueFamilyIndices = NULL;
+    vk_swapchain_desc.preTransform = surface_caps.currentTransform;
+    vk_swapchain_desc.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+    vk_swapchain_desc.presentMode = VK_PRESENT_MODE_FIFO_KHR;
+    vk_swapchain_desc.clipped = VK_TRUE;
+    vk_swapchain_desc.oldSwapchain = VK_NULL_HANDLE;
+    if (vkCreateSwapchainKHR(vk_device, &vk_swapchain_desc, NULL, &vk_swapchain) < 0)
+        goto fail;
+
+    fence_desc.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+    fence_desc.pNext = NULL;
+    fence_desc.flags = 0;
+    if (vkCreateFence(vk_device, &fence_desc, NULL, &vk_fence) < 0)
+        goto fail;
+
+    if (vkGetSwapchainImagesKHR(vk_device, vk_swapchain, &image_count, NULL) < 0
+            || !(vk_images = calloc(image_count, sizeof(*vk_images))))
+        goto fail;
+
+    if (vkGetSwapchainImagesKHR(vk_device, vk_swapchain, &image_count, vk_images) < 0)
+    {
+        free(vk_images);
+        goto fail;
+    }
+
+    if (!(swapchain = malloc(offsetof(struct demo_swapchain, buffers[image_count]))))
+    {
+        free(vk_images);
+        goto fail;
+    }
+    swapchain->vk_surface = vk_surface;
+    swapchain->vk_swapchain = vk_swapchain;
+    swapchain->vk_fence = vk_fence;
+    swapchain->vk_instance = vk_instance;
+    swapchain->vk_device = vk_device;
+
+    vkAcquireNextImageKHR(vk_device, vk_swapchain, UINT64_MAX,
+            VK_NULL_HANDLE, vk_fence, &swapchain->current_buffer);
+    vkWaitForFences(vk_device, 1, &vk_fence, VK_TRUE, UINT64_MAX);
+    vkResetFences(vk_device, 1, &vk_fence);
+
+    resource_create_info.type = VKD3D_STRUCTURE_TYPE_IMAGE_RESOURCE_CREATE_INFO;
+    resource_create_info.next = NULL;
+    resource_create_info.desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+    resource_create_info.desc.Alignment = 0;
+    resource_create_info.desc.Width = desc->width;
+    resource_create_info.desc.Height = desc->height;
+    resource_create_info.desc.DepthOrArraySize = 1;
+    resource_create_info.desc.MipLevels = 1;
+    resource_create_info.desc.Format = desc->format;
+    resource_create_info.desc.SampleDesc.Count = 1;
+    resource_create_info.desc.SampleDesc.Quality = 0;
+    resource_create_info.desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+    resource_create_info.desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
+    resource_create_info.flags = VKD3D_RESOURCE_INITIAL_STATE_TRANSITION | VKD3D_RESOURCE_PRESENT_STATE_TRANSITION;
+    resource_create_info.present_state = D3D12_RESOURCE_STATE_PRESENT;
+    for (i = 0; i < image_count; ++i)
+    {
+        resource_create_info.vk_image = vk_images[i];
+        if (FAILED(vkd3d_create_image_resource(d3d12_device, &resource_create_info, &swapchain->buffers[i])))
+        {
+            for (j = 0; j < i; ++j)
+            {
+                ID3D12Resource_Release(swapchain->buffers[j]);
+            }
+            free(swapchain);
+            free(vk_images);
+            goto fail;
+        }
+    }
+    swapchain->buffer_count = image_count;
+    free(vk_images);
+    ID3D12Device_Release(d3d12_device);
+
+    ID3D12CommandQueue_AddRef(swapchain->command_queue = command_queue);
+
+    [pool release];
+
+    return swapchain;
+
+fail:
+    if (vk_fence != VK_NULL_HANDLE)
+        vkDestroyFence(vk_device, vk_fence, NULL);
+    if (vk_swapchain != VK_NULL_HANDLE)
+        vkDestroySwapchainKHR(vk_device, vk_swapchain, NULL);
+    vkDestroySurfaceKHR(vk_instance, vk_surface, NULL);
+    ID3D12Device_Release(d3d12_device);
+    [pool release];
+    return NULL;
+}
+
+static inline unsigned int demo_swapchain_get_current_back_buffer_index(struct demo_swapchain *swapchain)
+{
+    return swapchain->current_buffer;
+}
+
+static inline ID3D12Resource *demo_swapchain_get_back_buffer(struct demo_swapchain *swapchain, unsigned int index)
+{
+    ID3D12Resource *resource = NULL;
+
+    if (index < swapchain->buffer_count && (resource = swapchain->buffers[index]))
+        ID3D12Resource_AddRef(resource);
+
+    return resource;
+}
+
+static inline void demo_swapchain_present(struct demo_swapchain *swapchain)
+{
+    VkPresentInfoKHR present_desc;
+    VkQueue vk_queue;
+
+    present_desc.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+    present_desc.pNext = NULL;
+    present_desc.waitSemaphoreCount = 0;
+    present_desc.pWaitSemaphores = NULL;
+    present_desc.swapchainCount = 1;
+    present_desc.pSwapchains = &swapchain->vk_swapchain;
+    present_desc.pImageIndices = &swapchain->current_buffer;
+    present_desc.pResults = NULL;
+
+    vk_queue = vkd3d_acquire_vk_queue(swapchain->command_queue);
+    vkQueuePresentKHR(vk_queue, &present_desc);
+    vkd3d_release_vk_queue(swapchain->command_queue);
+
+    vkAcquireNextImageKHR(swapchain->vk_device, swapchain->vk_swapchain, UINT64_MAX,
+            VK_NULL_HANDLE, swapchain->vk_fence, &swapchain->current_buffer);
+    vkWaitForFences(swapchain->vk_device, 1, &swapchain->vk_fence, VK_TRUE, UINT64_MAX);
+    vkResetFences(swapchain->vk_device, 1, &swapchain->vk_fence);
+}
+
+static inline void demo_swapchain_destroy(struct demo_swapchain *swapchain)
+{
+    unsigned int i;
+
+    ID3D12CommandQueue_Release(swapchain->command_queue);
+    for (i = 0; i < swapchain->buffer_count; ++i)
+    {
+        ID3D12Resource_Release(swapchain->buffers[i]);
+    }
+    vkDestroyFence(swapchain->vk_device, swapchain->vk_fence, NULL);
+    vkDestroySwapchainKHR(swapchain->vk_device, swapchain->vk_swapchain, NULL);
+    vkDestroySurfaceKHR(swapchain->vk_instance, swapchain->vk_surface, NULL);
+    free(swapchain);
+}
+
+static inline HANDLE demo_create_event(void)
+{
+    return vkd3d_create_event();
+}
+
+static inline unsigned int demo_wait_event(HANDLE event, unsigned int ms)
+{
+    return vkd3d_wait_event(event, ms);
+}
+
+static inline void demo_destroy_event(HANDLE event)
+{
+    vkd3d_destroy_event(event);
+}
+
diff --git a/libs/vkd3d-utils/vkd3d_utils_main.c b/libs/vkd3d-utils/vkd3d_utils_main.c
index 2c4d89a..fce7cf7 100644
--- a/libs/vkd3d-utils/vkd3d_utils_main.c
+++ b/libs/vkd3d-utils/vkd3d_utils_main.c
@@ -34,7 +34,11 @@ HRESULT WINAPI D3D12CreateDevice(IUnknown *adapter,
     static const char * const instance_extensions[] =
     {
         VK_KHR_SURFACE_EXTENSION_NAME,
+#ifdef __APPLE__
+        VK_MVK_MACOS_SURFACE_EXTENSION_NAME,
+#else
         VK_KHR_XCB_SURFACE_EXTENSION_NAME,
+#endif
     };
     static const char * const device_extensions[] =
     {
diff --git a/libs/vkd3d-utils/vkd3d_utils_private.h b/libs/vkd3d-utils/vkd3d_utils_private.h
index 6aa0df6..a59f471 100644
--- a/libs/vkd3d-utils/vkd3d_utils_private.h
+++ b/libs/vkd3d-utils/vkd3d_utils_private.h
@@ -20,7 +20,12 @@
 #define __VKD3D_UTILS_PRIVATE_H
 
 #define VK_NO_PROTOTYPES
+
+#ifdef __APPLE__
+#define VK_USE_PLATFORM_MACOS_MVK
+#else
 #define VK_USE_PLATFORM_XCB_KHR
+#endif
 
 #include <pthread.h>
 #include <vkd3d.h>
diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c
index 6fa0017..0052eba 100644
--- a/libs/vkd3d/device.c
+++ b/libs/vkd3d/device.c
@@ -205,6 +205,12 @@ static HRESULT vkd3d_init_instance_caps(struct vkd3d_instance *instance,
     return S_OK;
 }
 
+#ifdef HAVE_MOLTENVK_VK_MVK_MOLTENVK_H
+#define VULKAN_SO_NAME "libMoltenVK.dylib"
+#else
+#define VULKAN_SO_NAME "libvulkan.so.1"
+#endif
+
 static HRESULT vkd3d_init_vk_global_procs(struct vkd3d_instance *instance,
         PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr)
 {
@@ -212,9 +218,9 @@ static HRESULT vkd3d_init_vk_global_procs(struct vkd3d_instance *instance,
 
     if (!vkGetInstanceProcAddr)
     {
-        if (!(instance->libvulkan = dlopen("libvulkan.so.1", RTLD_NOW)))
+        if (!(instance->libvulkan = dlopen(VULKAN_SO_NAME, RTLD_NOW)))
         {
-            ERR("Failed to load libvulkan.\n");
+            ERR("Failed to load libvulkan: %s\n", dlerror());
             return E_FAIL;
         }
 
diff --git a/tests/vkd3d_api.c b/tests/vkd3d_api.c
index e2d9d01..3980b4e 100644
--- a/tests/vkd3d_api.c
+++ b/tests/vkd3d_api.c
@@ -19,8 +19,12 @@
 #define COBJMACROS
 #define INITGUID
 #define WIDL_C_INLINE_WRAPPERS
+#ifdef __APPLE__
+#define VK_USE_PLATFORM_MACOS_MVK
+#else
 #define VK_USE_PLATFORM_XCB_KHR
 #define VK_USE_PLATFORM_XLIB_KHR
+#endif
 #include "vkd3d_test.h"
 #include <vkd3d.h>
 
@@ -215,8 +219,12 @@ static void test_additional_instance_extensions(void)
     struct vulkan_extension extensions[] =
     {
         {VK_KHR_SURFACE_EXTENSION_NAME},
+#ifdef __APPLE__
+        {VK_MVK_MACOS_SURFACE_EXTENSION_NAME},
+#else
         {VK_KHR_XCB_SURFACE_EXTENSION_NAME},
         {VK_KHR_XLIB_SURFACE_EXTENSION_NAME},
+#endif
     };
 
     const char *enabled_extensions[ARRAY_SIZE(extensions)];
@@ -249,6 +257,13 @@ static void test_additional_instance_extensions(void)
         if (!extensions[i].is_supported)
             continue;
 
+#ifdef __APPLE__
+        if (!strcmp(extensions[i].name, VK_MVK_MACOS_SURFACE_EXTENSION_NAME))
+        {
+            pfn = vkGetInstanceProcAddr(vk_instance, "vkCreateMacOSSurfaceMVK");
+            ok(pfn, "Failed to get proc addr for vkCreateMacOSSurfaceMVK.\n");
+        }
+#else
         if (!strcmp(extensions[i].name, VK_KHR_XCB_SURFACE_EXTENSION_NAME))
         {
             pfn = vkGetInstanceProcAddr(vk_instance, "vkCreateXcbSurfaceKHR");
@@ -259,6 +274,7 @@ static void test_additional_instance_extensions(void)
             pfn = vkGetInstanceProcAddr(vk_instance, "vkCreateXlibSurfaceKHR");
             ok(pfn, "Failed to get proc addr for vkCreateXlibSurfaceKHR.\n");
         }
+#endif
     }
 
     refcount = vkd3d_instance_decref(instance);
-- 
2.18.0




More information about the wine-devel mailing list