demos: Make the demos work in the macOS build.

This commit is contained in:
Henri Verbeet
2025-05-03 17:27:14 +02:00
parent 7b07d77396
commit b389fa89e4
Notes: Henri Verbeet 2025-05-06 19:05:45 +02:00
Approved-by: Henri Verbeet (@hverbeet)
Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/1482
9 changed files with 792 additions and 7 deletions

View File

@@ -43,13 +43,17 @@
#define DEMO_ASM_PUSHSECTION ".section rdata\n\t"
#define DEMO_ASM_POPSECTION ".text\n\t"
#define DEMO_ASM_OBJECT_TYPE(name)
#elif defined(__APPLE__)
#define DEMO_ASM_PUSHSECTION ".pushsection __TEXT,__const\n\t"
#define DEMO_ASM_POPSECTION ".popsection\n\t"
#define DEMO_ASM_OBJECT_TYPE(name)
#else
#define DEMO_ASM_PUSHSECTION ".pushsection .rodata\n\t"
#define DEMO_ASM_POPSECTION ".popsection\n\t"
#define DEMO_ASM_OBJECT_TYPE(name) ".type "name", @object\n\t"
#endif
#if defined(__WIN32__) && defined(__i386__)
#if (defined(__WIN32__) && defined(__i386__)) || defined(__APPLE__)
#define DEMO_ASM_NAME(name) "_"#name
#else
#define DEMO_ASM_NAME(name) #name

310
demos/demo_macos.h Normal file
View File

@@ -0,0 +1,310 @@
/*
* Copyright 2025 Henri Verbeet
*
* 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
*/
typedef long NSInteger;
typedef unsigned long NSUInteger;
typedef struct NSPoint
{
double x, y;
} NSPoint;
typedef struct NSRect
{
double x, y;
double w, h;
} NSRect;
#define BOOL OBJC_BOOL
#include "private/appkit.h"
#include "private/foundation.h"
#include "private/quartzcore.h"
#undef BOOL
extern const id NSDefaultRunLoopMode;
enum NSBackingStoreType
{
NSBackingStoreBuffered = 2,
};
enum NSEventType
{
NSEventTypeKeyDown = 0xa,
NSEventTypeApplicationDefined = 0xf,
};
enum NSWindowStyleMask
{
NSWindowStyleMaskBorderless = 0x0000,
NSWindowStyleMaskTitled = 0x0001,
NSWindowStyleMaskClosable = 0x0002,
NSWindowStyleMaskMiniaturizable = 0x0004,
NSWindowStyleMaskResizable = 0x0008,
NSWindowStyleMaskUtilityWindow = 0x0010,
NSWindowStyleMaskDocModalWindow = 0x0040,
NSWindowStyleMaskNonactivatingPanel = 0x0080,
NSWindowStyleMaskUnifiedTitleAndToolbar = 0x1000,
NSWindowStyleMaskHUDWindow = 0x2000,
NSWindowStyleMaskFullScreen = 0x4000,
NSWindowStyleMaskFullSizeContentView = 0x8000,
};
enum
{
DemoWindowDestroyed,
};
struct demo_window_macos
{
struct demo_window w;
id window;
id layer;
};
static struct demo_window_macos *demo_macos_find_macos_window(struct demo *demo, id window)
{
size_t i;
for (i = 0; i < demo->window_count; ++i)
{
struct demo_window_macos *window_macos = CONTAINING_RECORD(demo->windows[i], struct demo_window_macos, w);
if (window_macos->window == window)
return window_macos;
}
return NULL;
}
static VkSurfaceKHR demo_window_macos_create_vk_surface(struct demo_window *window, VkInstance vk_instance)
{
struct demo_window_macos *window_macos = CONTAINING_RECORD(window, struct demo_window_macos, w);
struct VkMetalSurfaceCreateInfoEXT surface_desc;
VkSurfaceKHR vk_surface;
id l, v;
l = window_macos->layer = CAMetalLayer_layer();
CAMetalLayer_setContentsScale(l, NSScreen_backingScaleFactor(NSScreen_mainScreen()));
v = NSWindow_contentView(window_macos->window);
NSView_setLayer(v, l);
NSView_setWantsLayer(v, true);
surface_desc.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
surface_desc.pNext = NULL;
surface_desc.flags = 0;
surface_desc.pLayer = l;
if (vkCreateMetalSurfaceEXT(vk_instance, &surface_desc, NULL, &vk_surface) < 0)
return VK_NULL_HANDLE;
return vk_surface;
}
static void demo_window_macos_destroy(struct demo_window *window)
{
struct demo_window_macos *window_macos = CONTAINING_RECORD(window, struct demo_window_macos, w);
NSWindow_close(window_macos->window);
}
static void demo_window_macos_destroyed(struct demo_window_macos *window_macos)
{
CAMetalLayer_release(window_macos->layer);
NSWindow_release(window_macos->window);
demo_window_cleanup(&window_macos->w);
free(window_macos);
}
static struct demo_window *demo_window_macos_create(struct demo *demo,
const char *title, unsigned int width, unsigned int height, void *user_data)
{
unsigned long style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable;
struct demo_window_macos *window_macos;
NSRect r = {0, 0, width, height};
double scale;
id w, s;
if (!(window_macos = malloc(sizeof(*window_macos))))
return NULL;
if (!demo_window_init(&window_macos->w, demo, user_data,
demo_window_macos_create_vk_surface, demo_window_macos_destroy))
{
free(window_macos);
return NULL;
}
s = NSScreen_mainScreen();
scale = NSScreen_backingScaleFactor(s);
r.w /= scale;
r.h /= scale;
w = window_macos->window = class_createInstance(objc_getClass("DemoWindow"), 0);
NSWindow_initWithContentRect(w, r, style, NSBackingStoreBuffered, true, s);
NSWindow_setReleasedWhenClosed(w, false);
NSWindow_setDelegate(w, w);
NSWindow_center(w);
NSWindow_setTitle(w, NSString_stringWithUTF8String(title));
NSWindow_makeKeyAndOrderFront(w, nil);
window_macos->layer = nil;
return &window_macos->w;
}
static void demo_macos_get_dpi(struct demo *demo, double *dpi_x, double *dpi_y)
{
*dpi_x = *dpi_y = 96.0 * NSScreen_backingScaleFactor(NSScreen_mainScreen());
}
static demo_key demo_key_from_nsevent(id event)
{
enum vkey
{
kVK_ANSI_A = 0x00,
kVK_Escape = 0x35,
kVK_LeftArrow = 0x7b,
kVK_RightArrow = 0x7c,
kVK_DownArrow = 0x7d,
kVK_UpArrow = 0x7e,
} vkey;
size_t i;
static const struct
{
enum vkey vkey;
demo_key demo_key;
}
lookup[] =
{
{kVK_ANSI_A, 'a'},
{kVK_Escape, DEMO_KEY_ESCAPE},
{kVK_LeftArrow, DEMO_KEY_LEFT},
{kVK_RightArrow, DEMO_KEY_RIGHT},
{kVK_UpArrow, DEMO_KEY_UP},
{kVK_DownArrow, DEMO_KEY_DOWN},
};
vkey = NSEvent_keyCode(event);
for (i = 0; i < ARRAY_SIZE(lookup); ++i)
{
if (lookup[i].vkey == vkey)
return lookup[i].demo_key;
}
return DEMO_KEY_UNKNOWN;
}
static void demo_macos_process_events(struct demo *demo)
{
struct demo_window_macos *window_macos;
struct demo_window *window;
id a, event;
size_t i;
for (i = 0; i < demo->window_count; ++i)
{
if ((window = demo->windows[i])->expose_func)
window->expose_func(window, window->user_data);
}
a = NSApplication_sharedApplication();
while (demo->window_count)
{
if (!demo->idle_func)
{
if (!(event = NSApplication_nextEventMatchingMask(a, ~(uint64_t)0,
NSDate_distantFuture(), NSDefaultRunLoopMode, true)))
break;
}
else if (!(event = NSApplication_nextEventMatchingMask(a, ~(uint64_t)0, nil, NSDefaultRunLoopMode, true)))
{
demo->idle_func(demo, demo->user_data);
continue;
}
switch (NSEvent_type(event))
{
case NSEventTypeKeyDown:
if (NSMenu_performKeyEquivalent(NSApplication_mainMenu(a), event))
continue;
if (!(window_macos = demo_macos_find_macos_window(demo, NSEvent_window(event)))
|| !window_macos->w.key_press_func)
break;
window_macos->w.key_press_func(&window_macos->w,
demo_key_from_nsevent(event), window_macos->w.user_data);
continue;
case NSEventTypeApplicationDefined:
if (NSEvent_subtype(event) != DemoWindowDestroyed
|| !(window_macos = demo_macos_find_macos_window(demo, NSEvent_window(event))))
break;
demo_window_macos_destroyed(window_macos);
continue;
}
NSApplication_sendEvent(a, event);
}
}
static void DemoWindow_windowWillClose(id window, SEL sel, id notification)
{
id event;
event = NSEvent_otherEventWithType(NSEventTypeApplicationDefined, (NSPoint){0.0, 0.0},
0, 0.0, NSWindow_windowNumber(window), nil, DemoWindowDestroyed, 0, 0);
NSApplication_postEvent(NSApplication_sharedApplication(), event, true);
}
static void demo_macos_cleanup(struct demo *demo)
{
}
static bool demo_macos_init(struct demo_macos *macos)
{
id application, item, menu, submenu;
Class c;
if ((c = objc_allocateClassPair(objc_getClass("NSWindow"), "DemoWindow", 0)))
{
class_addMethod(c, sel_registerName("windowWillClose:"), (IMP)DemoWindow_windowWillClose, "v@:@");
objc_registerClassPair(c);
}
application = NSApplication_sharedApplication();
NSApplication_setActivationPolicy(application, 0);
menu = NSMenu_new();
submenu = NSMenu_new();
NSMenu_addItemWithTitle(submenu, NSString_stringWithUTF8String("Quit"),
sel_registerName("terminate:"), NSString_stringWithUTF8String("q"));
item = NSMenuItem_new();
NSMenuItem_setSubmenu(item, submenu);
NSMenu_release(submenu);
NSMenu_addItem(menu, item);
NSMenuItem_release(item);
NSApplication_setMainMenu(application, menu);
NSMenu_release(menu);
NSApplication_finishLaunching(application);
return true;
}

View File

@@ -19,6 +19,9 @@
#include "config.h"
#define VK_NO_PROTOTYPES
#ifdef __APPLE__
# define VK_USE_PLATFORM_METAL_EXT
#endif
#ifdef _WIN32
# define VK_USE_PLATFORM_WIN32_KHR
#endif
@@ -44,6 +47,9 @@
DECLARE_VK_PFN(vkAcquireNextImageKHR)
DECLARE_VK_PFN(vkCreateFence)
DECLARE_VK_PFN(vkCreateSwapchainKHR)
#ifdef __APPLE__
DECLARE_VK_PFN(vkCreateMetalSurfaceEXT)
#endif
#ifdef _WIN32
DECLARE_VK_PFN(vkCreateWin32SurfaceKHR)
#endif
@@ -61,6 +67,12 @@ DECLARE_VK_PFN(vkQueuePresentKHR)
DECLARE_VK_PFN(vkResetFences)
DECLARE_VK_PFN(vkWaitForFences)
struct demo_macos
{
#ifdef __APPLE__
#endif
};
struct demo_win32
{
#ifdef _WIN32
@@ -83,6 +95,7 @@ struct demo
{
union
{
struct demo_macos macos;
struct demo_win32 win32;
struct demo_xcb xcb;
} u;
@@ -167,6 +180,9 @@ static inline void demo_window_cleanup(struct demo_window *window)
demo_remove_window(window->demo, window);
}
#ifdef __APPLE__
# include "demo_macos.h"
#endif
#ifdef _WIN32
# include "demo_win32.h"
#endif
@@ -200,6 +216,9 @@ static void load_vulkan_procs(void)
LOAD_VK_PFN(vkAcquireNextImageKHR)
LOAD_VK_PFN(vkCreateFence)
LOAD_VK_PFN(vkCreateSwapchainKHR)
#ifdef __APPLE__
LOAD_VK_PFN(vkCreateMetalSurfaceEXT)
#endif
#ifdef _WIN32
LOAD_VK_PFN(vkCreateWin32SurfaceKHR)
#endif
@@ -244,6 +263,15 @@ static inline void demo_cleanup(struct demo *demo)
static inline bool demo_init(struct demo *demo, void *user_data)
{
#ifdef __APPLE__
if (demo_macos_init(&demo->u.macos))
{
demo->create_window = demo_window_macos_create;
demo->get_dpi = demo_macos_get_dpi;
demo->process_events = demo_macos_process_events;
demo->cleanup = demo_macos_cleanup;
}
#endif
#ifdef _WIN32
if (demo_win32_init(&demo->u.win32))
{

143
demos/make_objc Executable file
View File

@@ -0,0 +1,143 @@
#!/usr/bin/perl -w
#
# Copyright 2025 Henri Verbeet
#
# 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
use strict;
use warnings;
use JSON;
use open ':utf8';
binmode STDOUT, ':utf8';
sub method_name($)
{
shift->{selector} =~ s/(:.*)//r;
}
sub method_parameters($$)
{
my ($method, $method_type) = @_;
my $parameters = join ", ", $method_type eq "class" ? () : "id self",
map {"$_->{type} $_->{name}"} @{$method->{parameters}};
length $parameters ? $parameters : "void";
}
sub send_function($)
{
shift->{'return-float'} ? "objc_msgSend_fpret" : "objc_msgSend";
}
sub invocation_type($$)
{
my ($method, $method_type) = @_;
"$method->{'return-type'} (*)(" . join(", ", $method_type eq "class" ? "Class" : "id", "SEL",
map {$_->{type}} @{$method->{parameters}}) . ")";
}
sub invocation_parameters($$$)
{
my ($method, $interface_name, $method_type) = @_;
join ", ", $method_type eq "class" ? "objc_getClass(\"$interface_name\")" : "self",
"sel_registerName(\"$method->{selector}\")", map {$_->{name}} @{$method->{parameters}};
}
sub invocation($$$)
{
my ($method, $interface_name, $method_type) = @_;
($method->{'return-type'} eq "void" ? "" : "return ")
. "((${\invocation_type $method, $method_type})f)"
. "(${\invocation_parameters $method, $interface_name, $method_type});";
}
sub print_method($$$)
{
my ($method, $interface_name, $method_type) = @_;
print "static inline $method->{'return-type'} "
. "${interface_name}_${\method_name $method}(${\method_parameters $method, $method_type})\n";
print "{\n";
print " void *f = ${\send_function $method};\n";
print " ${\invocation $method, $interface_name, $method_type}\n";
print "}\n\n";
}
sub print_property($$)
{
my ($property, $interface_name) = @_;
my $method =
{
'return-type' => $property->{type},
'return-float' => $property->{float},
selector => $property->{getter} // $property->{name},
};
my $method_type = $property->{class} ? "class" : "instance";
print_method $method, $interface_name, $method_type;
if (!$property->{readonly})
{
$method->{'return-type'} = "void";
$method->{'return-float'} = 0;
$method->{selector} = "set${\ucfirst $property->{name}}:";
$method->{parameters} = [$property];
print_method $method, $interface_name, $method_type;
}
}
sub print_interface(_)
{
my ($interface) = @_;
print_method $_, $interface->{name}, "class" foreach (@{$interface->{'class-methods'}});
print_method $_, $interface->{name}, "instance" foreach (@{$interface->{'instance-methods'}});
print_property $_, $interface->{name} foreach (@{$interface->{properties}});
}
sub print_header($)
{
my ($grammar) = @_;
my $guard = "__VKD3D_${\uc $grammar->{name}}_H__";
print "/*\n";
print " * This file is automatically generated.\n";
print " * The original source is covered by the following license:\n";
print " *\n";
print map {" * $_" =~ s/ +$//r . "\n"} @{$grammar->{copyright}};
print " */\n\n";
print "#ifndef $guard\n";
print "#define $guard\n\n";
print "#include <objc/objc-runtime.h>\n\n";
print_interface foreach (@{$grammar->{interfaces}});
print "#endif /* $guard */\n";
}
die "No input file specified.\n" unless @ARGV;
print_header do
{
local $/;
open my $fh, '<', $ARGV[0] or die $!;
decode_json <$fh>;
};