summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorskallweitNV <64953474+skallweitNV@users.noreply.github.com>2023-12-19 00:16:14 +0100
committerGitHub <noreply@github.com>2023-12-18 15:16:14 -0800
commit93b8f68b2e9ddc450ce63f1b6e1806960312d803 (patch)
treed5c9c38efe1e7c86637c4be6157595b44a5c4856
parentb6da04424aff71ddba9629c94401a9a897b152a0 (diff)
macos/vulkan support (#3418)
-rw-r--r--CMakeLists.txt17
-rw-r--r--build/visual-studio/gfx/gfx.vcxproj1
-rw-r--r--build/visual-studio/gfx/gfx.vcxproj.filters3
-rw-r--r--cmake/Glob.cmake5
-rw-r--r--examples/hello-world/vulkan-api.cpp22
-rw-r--r--examples/platform-test/main.cpp102
-rw-r--r--premake5.lua20
-rw-r--r--slang-gfx.h8
-rw-r--r--slang.sln15
-rw-r--r--tools/gfx/apple/cocoa-util.h12
-rw-r--r--tools/gfx/apple/cocoa-util.mm15
-rw-r--r--tools/gfx/render.cpp2
-rw-r--r--tools/gfx/vulkan/vk-api.h4
-rw-r--r--tools/gfx/vulkan/vk-device.cpp13
-rw-r--r--tools/gfx/vulkan/vk-module.cpp3
-rw-r--r--tools/gfx/vulkan/vk-module.h2
-rw-r--r--tools/gfx/vulkan/vk-swap-chain.cpp16
-rw-r--r--tools/gfx/vulkan/vk-swap-chain.h17
-rw-r--r--tools/platform/apple/cocoa-window.mm618
-rw-r--r--tools/platform/placeholder/placeholder-window.cpp2
-rw-r--r--tools/platform/window.h8
21 files changed, 873 insertions, 32 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3ea4e1ef8..c2bd54565 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -64,11 +64,15 @@ auto_option(
NVAPI
"Enable NVAPI usage (Only available for builds targeting Windows)"
)
-auto_option(
- SLANG_ENABLE_XLIB
- X11
- "Build gfx and platform with Xlib to support windowed apps on Linux"
-)
+if(CMAKE_SYSTEM_NAME MATCHES "Linux")
+ auto_option(
+ SLANG_ENABLE_XLIB
+ X11
+ "Build gfx and platform with Xlib to support windowed apps on Linux"
+ )
+else()
+ set(SLANG_ENABLE_XLIB OFF)
+endif()
auto_option(
SLANG_ENABLE_AFTERMATH
Aftermath
@@ -417,6 +421,8 @@ slang_add_target(
core
imgui
$<$<BOOL:${SLANG_ENABLE_XLIB}>:X11::X11>
+ $<$<PLATFORM_ID:Darwin>:-framework Cocoa>
+ $<$<PLATFORM_ID:Darwin>:-framework QuartzCore>
${CMAKE_DL_LIBS}
EXTRA_COMPILE_DEFINITIONS_PRIVATE
$<$<BOOL:${SLANG_ENABLE_XLIB}>:SLANG_ENABLE_XLIB>
@@ -662,6 +668,7 @@ example(examples/cpu-hello-world )
example(examples/gpu-printing )
example(examples/hello-world LINK_WITH_PRIVATE Vulkan-Headers)
example(examples/model-viewer WIN32_EXECUTABLE)
+example(examples/platform-test WIN32_EXECUTABLE)
example(examples/ray-tracing WIN32_EXECUTABLE)
example(examples/ray-tracing-pipeline WIN32_EXECUTABLE)
example(examples/shader-object )
diff --git a/build/visual-studio/gfx/gfx.vcxproj b/build/visual-studio/gfx/gfx.vcxproj
index f761631f0..20d9ca28e 100644
--- a/build/visual-studio/gfx/gfx.vcxproj
+++ b/build/visual-studio/gfx/gfx.vcxproj
@@ -315,6 +315,7 @@ IF EXIST "$(SolutionDir)tools\gfx\slang.slang"\ (xcopy /Q /E /Y /I "$(SolutionDi
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\..\..\slang-gfx.h" />
+ <ClInclude Include="..\..\..\tools\gfx\apple\cocoa-util.h" />
<ClInclude Include="..\..\..\tools\gfx\command-encoder-com-forward.h" />
<ClInclude Include="..\..\..\tools\gfx\command-writer.h" />
<ClInclude Include="..\..\..\tools\gfx\cpu\cpu-base.h" />
diff --git a/build/visual-studio/gfx/gfx.vcxproj.filters b/build/visual-studio/gfx/gfx.vcxproj.filters
index ad0ef3c35..50baad7e5 100644
--- a/build/visual-studio/gfx/gfx.vcxproj.filters
+++ b/build/visual-studio/gfx/gfx.vcxproj.filters
@@ -12,6 +12,9 @@
<ClInclude Include="..\..\..\slang-gfx.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\apple\cocoa-util.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\tools\gfx\command-encoder-com-forward.h">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/cmake/Glob.cmake b/cmake/Glob.cmake
index c960e5011..16ce80dd5 100644
--- a/cmake/Glob.cmake
+++ b/cmake/Glob.cmake
@@ -22,6 +22,9 @@ function(slang_glob_sources var dir)
"*.natstepfilter"
"*.natjmc"
)
+ if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
+ list(APPEND patterns "*.mm")
+ endif()
list(TRANSFORM patterns PREPEND "${dir}/")
file(GLOB_RECURSE files CONFIGURE_DEPENDS ${patterns})
@@ -38,7 +41,7 @@ function(slang_glob_sources var dir)
list(FILTER files EXCLUDE REGEX "(^|/)d3d.*/.*")
endif()
- if(NOT CMAKE_SYSTEM_NAME MATCHES "Windows|Linux")
+ if(NOT CMAKE_SYSTEM_NAME MATCHES "Windows|Linux|Darwin")
list(FILTER files EXCLUDE REGEX "(^|/)vulkan/.*")
endif()
diff --git a/examples/hello-world/vulkan-api.cpp b/examples/hello-world/vulkan-api.cpp
index cc52956ce..3581563d4 100644
--- a/examples/hello-world/vulkan-api.cpp
+++ b/examples/hello-world/vulkan-api.cpp
@@ -41,6 +41,10 @@ int initializeVulkanDevice(VulkanAPI& api)
HMODULE module = ::LoadLibraryA(dynamicLibraryName);
api.vulkanLibraryHandle = (void*)module;
#define VK_API_GET_GLOBAL_PROC(x) api.x = (PFN_##x)GetProcAddress(module, #x);
+#elif SLANG_APPLE_FAMILY
+ dynamicLibraryName = "libvulkan.dylib";
+ api.vulkanLibraryHandle = dlopen(dynamicLibraryName, RTLD_NOW);
+#define VK_API_GET_GLOBAL_PROC(x) api.x = (PFN_##x)dlsym(api.vulkanLibraryHandle, #x);
#else
dynamicLibraryName = "libvulkan.so.1";
api.vulkanLibraryHandle = dlopen(dynamicLibraryName, RTLD_NOW);
@@ -78,10 +82,16 @@ int initializeVulkanDevice(VulkanAPI& api)
applicationInfo.engineVersion = 1;
applicationInfo.applicationVersion = 1;
const char* instanceExtensions[] = {
+#if SLANG_APPLE_FAMILY
+ VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
+#endif
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
};
VkInstanceCreateInfo instanceCreateInfo = {VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
+#if SLANG_APPLE_FAMILY
+ instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
+#endif
instanceCreateInfo.pApplicationInfo = &applicationInfo;
instanceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(instanceExtensions);
instanceCreateInfo.ppEnabledExtensionNames = &instanceExtensions[0];
@@ -150,14 +160,22 @@ int initializeVulkanDevice(VulkanAPI& api)
if (api.queueFamilyIndex == -1)
return -1;
+#if SLANG_APPLE_FAMILY
+ const char* deviceExtensions[] = {
+ "VK_KHR_portability_subset",
+ };
+#endif
+
VkDeviceQueueCreateInfo queueCreateInfo = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO};
float queuePriority = 0.0f;
queueCreateInfo.queueFamilyIndex = api.queueFamilyIndex;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
- deviceCreateInfo.enabledExtensionCount = 0;
- deviceCreateInfo.ppEnabledExtensionNames = nullptr;
+#if SLANG_APPLE_FAMILY
+ deviceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(deviceExtensions);
+ deviceCreateInfo.ppEnabledExtensionNames = &deviceExtensions[0];
+#endif
RETURN_ON_FAIL(api.vkCreateDevice(api.physicalDevice, &deviceCreateInfo, nullptr, &api.device));
// Load device functions.
diff --git a/examples/platform-test/main.cpp b/examples/platform-test/main.cpp
new file mode 100644
index 000000000..24673db1c
--- /dev/null
+++ b/examples/platform-test/main.cpp
@@ -0,0 +1,102 @@
+#include <slang.h>
+#include "tools/platform/window.h"
+#include "examples/example-base/example-base.h"
+
+using namespace gfx;
+using namespace Slang;
+
+struct PlatformTest : public WindowedAppBase
+{
+
+void onSizeChanged()
+{
+ printf("onSizeChanged\n");
+}
+
+void onFocus()
+{
+ printf("onFocus\n");
+}
+
+void onLostFocus()
+{
+ printf("onLostFocus\n");
+}
+
+void onKeyDown(platform::KeyEventArgs args)
+{
+ printf("onKeyDown(key=0x%02x, buttons=0x%02x)\n", args.key, args.buttons);
+}
+
+void onKeyUp(platform::KeyEventArgs args)
+{
+ printf("okKeyUp(key=0x%02x, buttons=0x%02x)\n", args.key, args.buttons);
+}
+
+void onKeyPress(platform::KeyEventArgs args)
+{
+ printf("onKeyPress(keyChar=0x%02x)\n", args.keyChar);
+}
+
+void onMouseMove(platform::MouseEventArgs args)
+{
+ printf("onMouseMove(x=%d, y=%d, delta=%d, buttons=0x%02x\n", args.x, args.y, args.delta, args.buttons);
+}
+
+void onMouseDown(platform::MouseEventArgs args)
+{
+ printf("onMouseDown(x=%d, y=%d, delta=%d, buttons=0x%02x\n", args.x, args.y, args.delta, args.buttons);
+}
+
+void onMouseUp(platform::MouseEventArgs args)
+{
+ printf("onMouseUp(x=%d, y=%d, delta=%d, buttons=0x%02x\n", args.x, args.y, args.delta, args.buttons);
+}
+
+void onMouseWheel(platform::MouseEventArgs args)
+{
+ printf("onMouseWheel(x=%d, y=%d, delta=%d, buttons=0x%02x\n", args.x, args.y, args.delta, args.buttons);
+}
+
+Slang::Result initialize()
+{
+ initializeBase("platform-test", 1024, 768);
+
+ gWindow->events.sizeChanged = [this]() { onSizeChanged(); };
+ gWindow->events.focus = [this]() { onFocus(); };
+ gWindow->events.lostFocus = [this]() { onLostFocus(); };
+ gWindow->events.keyDown = [this](const platform::KeyEventArgs& e) { onKeyDown(e); };
+ gWindow->events.keyUp = [this](const platform::KeyEventArgs& e) { onKeyUp(e); };
+ gWindow->events.keyPress = [this](const platform::KeyEventArgs& e) { onKeyPress(e); };
+ gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) { onMouseMove(e); };
+ gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) { onMouseDown(e); };
+ gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { onMouseUp(e); };
+ gWindow->events.mouseWheel = [this](const platform::MouseEventArgs& e) { onMouseWheel(e); };
+
+ return SLANG_OK;
+}
+
+virtual void renderFrame(int frameBufferIndex) override
+{
+ ComPtr<ICommandBuffer> commandBuffer = gTransientHeaps[frameBufferIndex]->createCommandBuffer();
+
+ auto renderEncoder = commandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameBufferIndex]);
+
+ gfx::Viewport viewport = {};
+ viewport.maxZ = 1.0f;
+ viewport.extentX = (float)windowWidth;
+ viewport.extentY = (float)windowHeight;
+ renderEncoder->setViewportAndScissor(viewport);
+
+ renderEncoder->endEncoding();
+ commandBuffer->close();
+ gQueue->executeCommandBuffer(commandBuffer);
+
+ gSwapchain->present();
+}
+
+};
+
+// This macro instantiates an appropriate main function to
+// run the application defined above.
+PLATFORM_UI_MAIN(innerMain<PlatformTest>)
diff --git a/premake5.lua b/premake5.lua
index 2034d004b..13c6279c6 100644
--- a/premake5.lua
+++ b/premake5.lua
@@ -509,9 +509,13 @@ function addSourceDir(path)
path .. "/*.natvis", -- Visual Studio debugger visualization files
path .. "/*.natstepfilter", -- Visual Studio debugger step filter files
path .. "/*.natjmc", -- Visual Studio debugger step filter files
-
-
}
+ if os.target() == "macosx" then
+ files { path .. "/*.mm" } -- Objective-C++ files
+ filter { "files:**.mm" }
+ compileas "Objective-C++"
+ filter {}
+ end
removefiles
{
"**/*.meta.slang.h",
@@ -846,6 +850,8 @@ example "hello-world"
--
-- Let's go ahead and set up the projects for our other example now.
+example "platform-test"
+
example "triangle"
example "ray-tracing"
@@ -1120,6 +1126,7 @@ tool "gfx"
files {"slang-gfx.h"}
-- Will compile across targets
+ addSourceDir "tools/gfx/apple"
addSourceDir "tools/gfx/cpu"
addSourceDir "tools/gfx/nvapi"
addSourceDir "tools/gfx/cuda"
@@ -1182,7 +1189,7 @@ tool "gfx"
elseif targetInfo.os == "mingw" or targetInfo.os == "cygwin" then
-- Don't support any render techs...
elseif os.target() == "macosx" then
- --addSourceDir "tools/gfx/open-gl"
+ addSourceDir "tools/gfx/vulkan"
else
-- Linux like
addSourceDir "tools/gfx/vulkan"
@@ -1194,6 +1201,10 @@ tool "gfx"
--addSourceDir "tools/gfx/open-gl"
end
+ if os.target() == "macosx" then
+ links { "Cocoa.framework" }
+ end
+
if enableXlib then
defines { "SLANG_ENABLE_XLIB" }
libdirs { "/usr/X11/lib" }
@@ -1247,10 +1258,13 @@ tool "platform"
addSourceDir "tools/platform"
addSourceDir "tools/platform/linux"
addSourceDir "tools/platform/windows"
+ addSourceDir "tools/platform/apple"
addSourceDir "tools/platform/placeholder"
-- Include windowing support on Windows.
if targetInfo.isWindows then
systemversion "latest"
+ elseif os.target() == "macosx" then
+ links { "Cocoa.framework", "QuartzCore.framework" }
else
if enableXlib then
defines { "SLANG_ENABLE_XLIB" }
diff --git a/slang-gfx.h b/slang-gfx.h
index c3ca7e2b3..9b45d2397 100644
--- a/slang-gfx.h
+++ b/slang-gfx.h
@@ -1460,6 +1460,7 @@ struct WindowHandle
{
Unknown,
Win32Handle,
+ NSViewHandle,
XLibHandle,
};
Type type;
@@ -1471,6 +1472,13 @@ struct WindowHandle
handle.handleValues[0] = (intptr_t)(hwnd);
return handle;
}
+ static WindowHandle FromNSView(void* nsview)
+ {
+ WindowHandle handle = {};
+ handle.type = WindowHandle::Type::NSViewHandle;
+ handle.handleValues[0] = (intptr_t)(nsview);
+ return handle;
+ }
static WindowHandle FromXWindow(void* xdisplay, uint32_t xwindow)
{
WindowHandle handle = {};
diff --git a/slang.sln b/slang.sln
index 9375165f0..c547b4e67 100644
--- a/slang.sln
+++ b/slang.sln
@@ -55,6 +55,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hello-world", "build\visual
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "model-viewer", "build\visual-studio\model-viewer\model-viewer.vcxproj", "{2F8724C6-1BC3-2730-84D5-3F277030D04A}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "platform-test", "build\visual-studio\platform-test\platform-test.vcxproj", "{F385D6A7-DF6C-989F-88BD-FEBC74831106}"
+EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ray-tracing", "build\visual-studio\ray-tracing\ray-tracing.vcxproj", "{71AC0F50-5DFD-FA91-8661-E95372118EFB}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ray-tracing-pipeline", "build\visual-studio\ray-tracing-pipeline\ray-tracing-pipeline.vcxproj", "{17BA8E32-034E-84DA-6C12-DE8E58C5BECC}"
@@ -383,6 +385,18 @@ Global
{2F8724C6-1BC3-2730-84D5-3F277030D04A}.Release|Win32.Build.0 = Release|Win32
{2F8724C6-1BC3-2730-84D5-3F277030D04A}.Release|x64.ActiveCfg = Release|x64
{2F8724C6-1BC3-2730-84D5-3F277030D04A}.Release|x64.Build.0 = Release|x64
+ {F385D6A7-DF6C-989F-88BD-FEBC74831106}.Debug|aarch64.ActiveCfg = Debug aarch64|ARM64
+ {F385D6A7-DF6C-989F-88BD-FEBC74831106}.Debug|aarch64.Build.0 = Debug aarch64|ARM64
+ {F385D6A7-DF6C-989F-88BD-FEBC74831106}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F385D6A7-DF6C-989F-88BD-FEBC74831106}.Debug|Win32.Build.0 = Debug|Win32
+ {F385D6A7-DF6C-989F-88BD-FEBC74831106}.Debug|x64.ActiveCfg = Debug|x64
+ {F385D6A7-DF6C-989F-88BD-FEBC74831106}.Debug|x64.Build.0 = Debug|x64
+ {F385D6A7-DF6C-989F-88BD-FEBC74831106}.Release|aarch64.ActiveCfg = Release aarch64|ARM64
+ {F385D6A7-DF6C-989F-88BD-FEBC74831106}.Release|aarch64.Build.0 = Release aarch64|ARM64
+ {F385D6A7-DF6C-989F-88BD-FEBC74831106}.Release|Win32.ActiveCfg = Release|Win32
+ {F385D6A7-DF6C-989F-88BD-FEBC74831106}.Release|Win32.Build.0 = Release|Win32
+ {F385D6A7-DF6C-989F-88BD-FEBC74831106}.Release|x64.ActiveCfg = Release|x64
+ {F385D6A7-DF6C-989F-88BD-FEBC74831106}.Release|x64.Build.0 = Release|x64
{71AC0F50-5DFD-FA91-8661-E95372118EFB}.Debug|aarch64.ActiveCfg = Debug aarch64|ARM64
{71AC0F50-5DFD-FA91-8661-E95372118EFB}.Debug|aarch64.Build.0 = Debug aarch64|ARM64
{71AC0F50-5DFD-FA91-8661-E95372118EFB}.Debug|Win32.ActiveCfg = Debug|Win32
@@ -612,6 +626,7 @@ Global
{57C81DD3-4304-213D-AC16-39349871C957} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231}
{010BE414-ED5B-CF56-16C0-BD18027062C0} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231}
{2F8724C6-1BC3-2730-84D5-3F277030D04A} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231}
+ {F385D6A7-DF6C-989F-88BD-FEBC74831106} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231}
{71AC0F50-5DFD-FA91-8661-E95372118EFB} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231}
{17BA8E32-034E-84DA-6C12-DE8E58C5BECC} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231}
{25512BFB-1138-EDF2-BA88-5310A64E6659} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231}
diff --git a/tools/gfx/apple/cocoa-util.h b/tools/gfx/apple/cocoa-util.h
new file mode 100644
index 000000000..80467d8a4
--- /dev/null
+++ b/tools/gfx/apple/cocoa-util.h
@@ -0,0 +1,12 @@
+#pragma once
+
+namespace gfx {
+
+// Utility functions for Cocoa
+struct CocoaUtil {
+
+ static void getNSViewRectSize(void* nsview, int* widthOut, int* heightOut);
+
+};
+
+}
diff --git a/tools/gfx/apple/cocoa-util.mm b/tools/gfx/apple/cocoa-util.mm
new file mode 100644
index 000000000..45b1c3df0
--- /dev/null
+++ b/tools/gfx/apple/cocoa-util.mm
@@ -0,0 +1,15 @@
+#include "cocoa-util.h"
+
+#import <Cocoa/Cocoa.h>
+
+namespace gfx {
+
+void CocoaUtil::getNSViewRectSize(void* nsview, int* widthOut, int* heightOut)
+{
+ NSView* view = (NSView*)nsview;
+ NSRect rect = [view frame];
+ *widthOut = rect.size.width;
+ *heightOut = rect.size.height;
+}
+
+} \ No newline at end of file
diff --git a/tools/gfx/render.cpp b/tools/gfx/render.cpp
index aad544eb4..6dd0c90dd 100644
--- a/tools/gfx/render.cpp
+++ b/tools/gfx/render.cpp
@@ -324,7 +324,7 @@ extern "C"
return SLANG_FAIL;
}
break;
-#elif SLANG_LINUX_FAMILY && !defined(__CYGWIN__)
+#elif (SLANG_LINUX_FAMILY || SLANG_APPLE_FAMILY) && !defined(__CYGWIN__)
case DeviceType::Default:
case DeviceType::Vulkan:
{
diff --git a/tools/gfx/vulkan/vk-api.h b/tools/gfx/vulkan/vk-api.h
index b7cbf13de..213921406 100644
--- a/tools/gfx/vulkan/vk-api.h
+++ b/tools/gfx/vulkan/vk-api.h
@@ -141,6 +141,10 @@ namespace gfx {
# define VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \
x(vkCreateWin32SurfaceKHR) \
/* */
+#elif SLANG_APPLE_FAMILY
+# define VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \
+ x(vkCreateMacOSSurfaceMVK) \
+ /* */
#elif SLANG_ENABLE_XLIB
# define VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \
x(vkCreateXlibSurfaceKHR) \
diff --git a/tools/gfx/vulkan/vk-device.cpp b/tools/gfx/vulkan/vk-device.cpp
index fe3680eda..66c2c811b 100644
--- a/tools/gfx/vulkan/vk-device.cpp
+++ b/tools/gfx/vulkan/vk-device.cpp
@@ -171,8 +171,11 @@ Result DeviceImpl::initVulkanInstanceAndDevice(
applicationInfo.engineVersion = 1;
applicationInfo.applicationVersion = 1;
- Array<const char*, 6> instanceExtensions;
+ Array<const char*, 7> instanceExtensions;
+#if SLANG_APPLE_FAMILY
+ instanceExtensions.add(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
+#endif
instanceExtensions.add(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
instanceExtensions.add(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME);
@@ -185,6 +188,8 @@ Result DeviceImpl::initVulkanInstanceAndDevice(
// instanceExtensions.add("VK_GOOGLE_surfaceless_query");
#if SLANG_WINDOWS_FAMILY
instanceExtensions.add(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
+#elif SLANG_APPLE_FAMILY
+ instanceExtensions.add(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
#elif defined(SLANG_ENABLE_XLIB)
instanceExtensions.add(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
@@ -195,6 +200,9 @@ Result DeviceImpl::initVulkanInstanceAndDevice(
instanceExtensions.add(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
VkInstanceCreateInfo instanceCreateInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
+#if SLANG_APPLE_FAMILY
+ instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
+#endif
instanceCreateInfo.pApplicationInfo = &applicationInfo;
instanceCreateInfo.enabledExtensionCount = (uint32_t)instanceExtensions.getCount();
instanceCreateInfo.ppEnabledExtensionNames = &instanceExtensions[0];
@@ -352,6 +360,9 @@ Result DeviceImpl::initVulkanInstanceAndDevice(
List<const char*> deviceExtensions;
deviceExtensions.add(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+#if SLANG_APPLE_FAMILY
+ deviceExtensions.add("VK_KHR_portability_subset");
+#endif
VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
deviceCreateInfo.queueCreateInfoCount = 1;
diff --git a/tools/gfx/vulkan/vk-module.cpp b/tools/gfx/vulkan/vk-module.cpp
index cff75569e..0e4df8e7f 100644
--- a/tools/gfx/vulkan/vk-module.cpp
+++ b/tools/gfx/vulkan/vk-module.cpp
@@ -32,6 +32,9 @@ Slang::Result VulkanModule::init(bool useSoftwareImpl)
dynamicLibraryName = useSoftwareImpl ? "vk_swiftshader.dll" : "vulkan-1.dll";
HMODULE module = ::LoadLibraryA(dynamicLibraryName);
m_module = (void*)module;
+#elif SLANG_APPLE_FAMILY
+ dynamicLibraryName = useSoftwareImpl ? "libvk_swiftshader.dylib" : "libvulkan.1.dylib";
+ m_module = dlopen(dynamicLibraryName, RTLD_NOW | RTLD_GLOBAL);
#else
dynamicLibraryName = useSoftwareImpl ? "libvk_swiftshader.so" : "libvulkan.so.1";
if (useSoftwareImpl)
diff --git a/tools/gfx/vulkan/vk-module.h b/tools/gfx/vulkan/vk-module.h
index 97a0c8aba..9b8fe0ed3 100644
--- a/tools/gfx/vulkan/vk-module.h
+++ b/tools/gfx/vulkan/vk-module.h
@@ -7,6 +7,8 @@
#if SLANG_WINDOWS_FAMILY
# define VK_USE_PLATFORM_WIN32_KHR 1
+#elif SLANG_APPLE_FAMILY
+# define VK_USE_PLATFORM_MACOS_MVK 1
#else
# define VK_USE_PLATFORM_XLIB_KHR 1
#endif
diff --git a/tools/gfx/vulkan/vk-swap-chain.cpp b/tools/gfx/vulkan/vk-swap-chain.cpp
index 384ca86ed..856fa2489 100644
--- a/tools/gfx/vulkan/vk-swap-chain.cpp
+++ b/tools/gfx/vulkan/vk-swap-chain.cpp
@@ -2,6 +2,7 @@
#include "vk-swap-chain.h"
#include "vk-util.h"
+#include "../apple/cocoa-util.h"
namespace gfx
{
@@ -38,6 +39,8 @@ void SwapchainImpl::getWindowSize(int* widthOut, int* heightOut) const
::GetClientRect((HWND)m_windowHandle.handleValues[0], &rc);
*widthOut = rc.right - rc.left;
*heightOut = rc.bottom - rc.top;
+#elif SLANG_APPLE_FAMILY
+ CocoaUtil::getNSViewRectSize((void*)m_windowHandle.handleValues[0], widthOut, heightOut);
#elif defined(SLANG_ENABLE_XLIB)
XWindowAttributes winAttr = {};
XGetWindowAttributes(
@@ -221,6 +224,12 @@ Result SwapchainImpl::init(DeviceImpl* renderer, const ISwapchain::Desc& desc, W
surfaceCreateInfo.hwnd = (HWND)window.handleValues[0];
SLANG_VK_RETURN_ON_FAIL(
m_api->vkCreateWin32SurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface));
+#elif SLANG_APPLE_FAMILY
+ VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo = {};
+ surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
+ surfaceCreateInfo.pView = (void*)window.handleValues[0];
+ SLANG_VK_RETURN_ON_FAIL(
+ m_api->vkCreateMacOSSurfaceMVK(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface));
#elif SLANG_ENABLE_XLIB
VkXlibSurfaceCreateInfoKHR surfaceCreateInfo = {};
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
@@ -347,7 +356,12 @@ int SwapchainImpl::acquireNextImage()
VK_NULL_HANDLE,
(uint32_t*)&m_currentImageIndex);
- if (result != VK_SUCCESS)
+ if (
+ result != VK_SUCCESS
+#if SLANG_APPLE_FAMILY
+ && result != VK_SUBOPTIMAL_KHR
+#endif
+ )
{
m_currentImageIndex = -1;
destroySwapchainAndImages();
diff --git a/tools/gfx/vulkan/vk-swap-chain.h b/tools/gfx/vulkan/vk-swap-chain.h
index 6a39e2afa..2c0fe62ed 100644
--- a/tools/gfx/vulkan/vk-swap-chain.h
+++ b/tools/gfx/vulkan/vk-swap-chain.h
@@ -23,23 +23,6 @@ public:
ISwapchain* getInterface(const Guid& guid);
public:
- struct PlatformDesc
- {};
-
-#if SLANG_WINDOWS_FAMILY
- struct WinPlatformDesc : public PlatformDesc
- {
- HINSTANCE m_hinstance;
- HWND m_hwnd;
- };
-#else
- struct XPlatformDesc : public PlatformDesc
- {
- Display* m_display;
- Window m_window;
- };
-#endif
-public:
VkSwapchainKHR m_swapChain;
VkSurfaceKHR m_surface;
VkSemaphore m_nextImageSemaphore; // Semaphore to signal after `acquireNextImage`.
diff --git a/tools/platform/apple/cocoa-window.mm b/tools/platform/apple/cocoa-window.mm
new file mode 100644
index 000000000..450087dd2
--- /dev/null
+++ b/tools/platform/apple/cocoa-window.mm
@@ -0,0 +1,618 @@
+#ifdef __APPLE__
+
+#include "../window.h"
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/CAMetalLayer.h>
+
+using namespace Slang;
+using namespace platform;
+
+namespace platform {
+class CocoaPlatformWindow;
+static KeyCode keyCodes[256];
+
+class CocoaAppContext
+{
+public:
+ static Window* mainWindow;
+ static bool isTerminated;
+};
+
+Window* CocoaAppContext::mainWindow;
+bool CocoaAppContext::isTerminated = false;
+}
+
+@interface WindowDelegate : NSObject <NSWindowDelegate>
+{
+ CocoaPlatformWindow* window;
+}
+
+- (instancetype)initWithPlatformWindow:(CocoaPlatformWindow*)platformWindow;
+
+@end
+
+@interface ContentView : NSView
+{
+ CocoaPlatformWindow* window;
+ NSTrackingArea* trackingArea;
+ int mouseX, mouseY;
+}
+
+- (instancetype)initWithPlatformWindow:(CocoaPlatformWindow*)platformWindow;
+
+@end
+
+namespace platform {
+
+class CocoaPlatformWindow : public Window
+{
+public:
+ NSWindow* window;
+ WindowDelegate* delegate;
+ ContentView* view;
+ CAMetalLayer* layer;
+ bool shouldClose = false;
+
+ CocoaPlatformWindow(const WindowDesc& desc);
+ ~CocoaPlatformWindow();
+
+ virtual void setClientSize(uint32_t width, uint32_t height) override;
+ virtual Rect getClientRect() override;
+ virtual void centerScreen() override;
+ virtual void close() override;
+ virtual bool getFocused() override;
+ virtual bool getVisible() override;
+ virtual WindowHandle getNativeHandle() override;
+ virtual void setText(Slang::String text) override;
+ virtual void show() override;
+ virtual void hide() override;
+ virtual int getCurrentDpi() override;
+};
+
+void getMousePosition(NSEvent* event, NSView* view, int& x, int& y)
+{
+ const NSRect contentRect = [view frame];
+ const NSPoint pos = [event locationInWindow];
+ x = (int)pos.x;
+ y = (int)(contentRect.size.height - pos.y);
+}
+
+ButtonState::Enum _addButtonState(ButtonState::Enum val, ButtonState::Enum newState)
+{
+ return (ButtonState::Enum)((int)val | (int)newState);
+}
+
+static ButtonState::Enum getModifierState(NSUInteger flags)
+{
+ ButtonState::Enum result = ButtonState::None;
+
+ if (flags & NSEventModifierFlagShift)
+ result = _addButtonState(result, ButtonState::Shift);
+ if (flags & NSEventModifierFlagControl)
+ result = _addButtonState(result, ButtonState::Control);
+ if (flags & NSEventModifierFlagOption)
+ result = _addButtonState(result, ButtonState::Alt);
+ return result;
+}
+
+static KeyCode getKeyCode(NSUInteger keyCode)
+{
+ return keyCode < 256 ? keyCodes[keyCode] : KeyCode::None;
+}
+
+}
+
+
+@implementation WindowDelegate
+
+- (instancetype)initWithPlatformWindow:(CocoaPlatformWindow*)platformWindow
+{
+ self = [super init];
+ if (self)
+ {
+ window = platformWindow;
+ }
+ return self;
+}
+
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
+{
+ return YES;
+}
+
+- (BOOL)windowShouldClose:(id)window_
+{
+ window->shouldClose = true;
+ if (CocoaAppContext::mainWindow == window)
+ CocoaAppContext::isTerminated = true;
+ return YES;
+}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
+{
+ printf("applicationShouldTerminate\n");
+ return NSTerminateCancel;
+}
+
+- (void)windowDidResize:(NSNotification*)notification
+{
+ window->events.sizeChanged();
+}
+
+- (void)windowDidBecomeKey:(NSNotification*)notification
+{
+ window->events.focus();
+}
+
+- (void)windowDidResignKey:(NSNotification*)notification
+{
+ window->events.lostFocus();
+}
+
+@end
+
+@implementation ContentView
+
+- (instancetype)initWithPlatformWindow:(CocoaPlatformWindow*)platformWindow
+{
+ self = [super init];
+ if (self)
+ {
+ window = platformWindow;
+ mouseX = 0;
+ mouseY = 0;
+ [self updateTrackingAreas];
+ }
+ return self;
+}
+
+- (void)updateTrackingAreas
+{
+ if (trackingArea != nil)
+ {
+ [self removeTrackingArea:trackingArea];
+ [trackingArea release];
+ }
+
+ const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |
+ NSTrackingMouseMoved |
+ NSTrackingActiveInKeyWindow |
+ NSTrackingEnabledDuringMouseDrag |
+ NSTrackingCursorUpdate |
+ NSTrackingInVisibleRect |
+ NSTrackingAssumeInside;
+
+ trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
+ options:options
+ owner:self
+ userInfo:nil];
+
+ [self addTrackingArea:trackingArea];
+ [super updateTrackingAreas];
+}
+
+- (BOOL)isOpaque
+{
+ return YES;
+}
+
+- (BOOL)canBecomeKeyView
+{
+ return YES;
+}
+
+- (BOOL)acceptsFirstResponder
+{
+ return YES;
+}
+
+- (BOOL)acceptsFirstMouse:(NSEvent*)event
+{
+ return YES;
+}
+
+- (BOOL)wantsUpdateLayer
+{
+ return YES;
+}
+
+- (void)mouseDown:(NSEvent*)event
+{
+ getMousePosition(event, self, mouseX, mouseY);
+ ButtonState::Enum buttons = ButtonState::LeftButton;
+ buttons = _addButtonState(buttons, getModifierState([event modifierFlags]));
+ window->events.mouseDown(MouseEventArgs{mouseX, mouseY, 0, buttons});
+}
+
+- (void)mouseUp:(NSEvent*)event
+{
+ getMousePosition(event, self, mouseX, mouseY);
+ ButtonState::Enum buttons = ButtonState::LeftButton;
+ buttons = _addButtonState(buttons, getModifierState([event modifierFlags]));
+ window->events.mouseUp(MouseEventArgs{mouseX, mouseY, 0, buttons});
+}
+
+- (void)rightMouseDown:(NSEvent*)event
+{
+ getMousePosition(event, self, mouseX, mouseY);
+ ButtonState::Enum buttons = ButtonState::RightButton;
+ buttons = _addButtonState(buttons, getModifierState([event modifierFlags]));
+ window->events.mouseDown(MouseEventArgs{mouseX, mouseY, 0, buttons});
+}
+
+- (void)rightMouseUp:(NSEvent*)event
+{
+ getMousePosition(event, self, mouseX, mouseY);
+ ButtonState::Enum buttons = ButtonState::RightButton;
+ buttons = _addButtonState(buttons, getModifierState([event modifierFlags]));
+ window->events.mouseUp(MouseEventArgs{mouseX, mouseY, 0, buttons});
+}
+
+- (void)otherMouseDown:(NSEvent *)event
+{
+ if ([event buttonNumber] == 2)
+ {
+ getMousePosition(event, self, mouseX, mouseY);
+ ButtonState::Enum buttons = ButtonState::MiddleButton;
+ buttons = _addButtonState(buttons, getModifierState([event modifierFlags]));
+ window->events.mouseDown(MouseEventArgs{mouseX, mouseY, 0, buttons});
+ }
+}
+
+- (void)otherMouseUp:(NSEvent *)event
+{
+ if ([event buttonNumber] == 2)
+ {
+ getMousePosition(event, self, mouseX, mouseY);
+ ButtonState::Enum buttons = ButtonState::MiddleButton;
+ buttons = _addButtonState(buttons, getModifierState([event modifierFlags]));
+ window->events.mouseUp(MouseEventArgs{mouseX, mouseY, 0, buttons});
+ }
+}
+
+- (void)mouseDragged:(NSEvent*)event
+{
+ [self mouseMoved:event];
+}
+
+- (void)rightMouseDragged:(NSEvent*)event
+{
+ [self mouseMoved:event];
+}
+
+- (void)otherMouseDragged:(NSEvent*)event
+{
+ [self mouseMoved:event];
+}
+
+- (void)mouseMoved:(NSEvent*)event
+{
+ getMousePosition(event, self, mouseX, mouseY);
+ window->events.mouseMove(MouseEventArgs{mouseX, mouseY, 0, getModifierState([event modifierFlags])});
+}
+
+- (void)scrollWheel:(NSEvent *)event
+{
+ double deltaX = [event scrollingDeltaX];
+ double deltaY = [event scrollingDeltaY];
+ if ([event hasPreciseScrollingDeltas])
+ {
+ deltaX *= 0.1;
+ deltaY *= 0.1;
+ }
+
+ int delta = (int)deltaY;
+
+ window->events.mouseWheel(MouseEventArgs{0, 0, delta, getModifierState([event modifierFlags])});
+}
+
+- (void)keyDown:(NSEvent *)event
+{
+ KeyCode key = getKeyCode([event keyCode]);
+ if (key == KeyCode::None)
+ return;
+ KeyEventArgs keyEventArgs = {key, 0, getModifierState([event modifierFlags]), false};
+ window->events.keyDown(keyEventArgs);
+ // if (!keyEventArgs.cancelEvent)
+ // [self interpretKeyEvents:@[event]];
+}
+
+- (void)keyUp:(NSEvent *)event
+{
+ KeyCode key = getKeyCode([event keyCode]);
+ if (key == KeyCode::None)
+ return;
+ KeyEventArgs keyEventArgs = {key, 0, getModifierState([event modifierFlags]), false};
+ window->events.keyUp(keyEventArgs);
+ // if (!keyEventArgs.cancelEvent)
+ // [self interpretKeyEvents:@[event]];
+}
+
+- (void)flagsChanged:(NSEvent *)event
+{
+ KeyCode key = getKeyCode([event keyCode]);
+ ButtonState::Enum buttons = getModifierState([event modifierFlags]);
+ ButtonState::Enum button = ButtonState::None;
+ if (key == KeyCode::Shift)
+ button = ButtonState::Shift;
+ else if (key == KeyCode::Ctrl)
+ button = ButtonState::Control;
+ else if (key == KeyCode::Alt)
+ button = ButtonState::Alt;
+
+ KeyEventArgs keyEventArgs = {key, 0, buttons, false};
+ if (button & buttons) {
+ window->events.keyDown(keyEventArgs);
+ } else {
+ window->events.keyUp(keyEventArgs);
+ }
+}
+
+@end
+
+
+static NSApplication *_application;
+
+
+namespace platform {
+
+void Application::init()
+{
+ _application = [NSApplication sharedApplication];
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
+ [NSApp activateIgnoringOtherApps:YES];
+
+ // Setup key translation table.
+ ::memset(keyCodes, (int)KeyCode::None, sizeof(keyCodes));
+
+ keyCodes[0x1D] = KeyCode::Key0;
+ keyCodes[0x12] = KeyCode::Key1;
+ keyCodes[0x13] = KeyCode::Key2;
+ keyCodes[0x14] = KeyCode::Key3;
+ keyCodes[0x15] = KeyCode::Key4;
+ keyCodes[0x17] = KeyCode::Key5;
+ keyCodes[0x16] = KeyCode::Key6;
+ keyCodes[0x1A] = KeyCode::Key7;
+ keyCodes[0x1C] = KeyCode::Key8;
+ keyCodes[0x19] = KeyCode::Key9;
+ keyCodes[0x00] = KeyCode::A;
+ keyCodes[0x0B] = KeyCode::B;
+ keyCodes[0x08] = KeyCode::C;
+ keyCodes[0x02] = KeyCode::D;
+ keyCodes[0x0E] = KeyCode::E;
+ keyCodes[0x03] = KeyCode::F;
+ keyCodes[0x05] = KeyCode::G;
+ keyCodes[0x04] = KeyCode::H;
+ keyCodes[0x22] = KeyCode::I;
+ keyCodes[0x26] = KeyCode::J;
+ keyCodes[0x28] = KeyCode::K;
+ keyCodes[0x25] = KeyCode::L;
+ keyCodes[0x2E] = KeyCode::M;
+ keyCodes[0x2D] = KeyCode::N;
+ keyCodes[0x1F] = KeyCode::O;
+ keyCodes[0x23] = KeyCode::P;
+ keyCodes[0x0C] = KeyCode::Q;
+ keyCodes[0x0F] = KeyCode::R;
+ keyCodes[0x01] = KeyCode::S;
+ keyCodes[0x11] = KeyCode::T;
+ keyCodes[0x20] = KeyCode::U;
+ keyCodes[0x09] = KeyCode::V;
+ keyCodes[0x0D] = KeyCode::W;
+ keyCodes[0x07] = KeyCode::X;
+ keyCodes[0x10] = KeyCode::Y;
+ keyCodes[0x06] = KeyCode::Z;
+
+ keyCodes[0x27] = KeyCode::Quote;
+ keyCodes[0x2A] = KeyCode::Backslash;
+ keyCodes[0x2B] = KeyCode::Comma;
+ keyCodes[0x18] = KeyCode::Plus;
+ keyCodes[0x32] = KeyCode::Tilde;
+ keyCodes[0x21] = KeyCode::LBracket;
+ keyCodes[0x1B] = KeyCode::Minus;
+ keyCodes[0x2F] = KeyCode::Dot;
+ keyCodes[0x1E] = KeyCode::RBracket;
+ keyCodes[0x29] = KeyCode::Semicolon;
+ keyCodes[0x2C] = KeyCode::Slash;
+
+ keyCodes[0x33] = KeyCode::Backspace;
+ keyCodes[0x75] = KeyCode::Delete;
+ keyCodes[0x7D] = KeyCode::Down;
+ keyCodes[0x77] = KeyCode::End;
+ keyCodes[0x24] = KeyCode::Return;
+ keyCodes[0x35] = KeyCode::Escape;
+ keyCodes[0x7A] = KeyCode::F1;
+ keyCodes[0x78] = KeyCode::F2;
+ keyCodes[0x63] = KeyCode::F3;
+ keyCodes[0x76] = KeyCode::F4;
+ keyCodes[0x60] = KeyCode::F5;
+ keyCodes[0x61] = KeyCode::F6;
+ keyCodes[0x62] = KeyCode::F7;
+ keyCodes[0x64] = KeyCode::F8;
+ keyCodes[0x65] = KeyCode::F9;
+ keyCodes[0x6D] = KeyCode::F10;
+ keyCodes[0x67] = KeyCode::F11;
+ keyCodes[0x6F] = KeyCode::F12;
+ keyCodes[0x73] = KeyCode::Home;
+ keyCodes[0x72] = KeyCode::Insert;
+ keyCodes[0x7B] = KeyCode::Left;
+ keyCodes[0x79] = KeyCode::PageDown;
+ keyCodes[0x74] = KeyCode::PageUp;
+ keyCodes[0x7C] = KeyCode::Right;
+ keyCodes[0x31] = KeyCode::Space;
+ keyCodes[0x30] = KeyCode::Tab;
+ keyCodes[0x7E] = KeyCode::Up;
+
+ keyCodes[0x38] = KeyCode::Shift;
+ keyCodes[0x3B] = KeyCode::Ctrl;
+ keyCodes[0x3A] = KeyCode::Alt;
+}
+
+void doEventsImpl(bool waitForEvents)
+{
+ NSEvent *event;
+ do {
+ event = [NSApp nextEventMatchingMask:NSEventMaskAny
+ untilDate:waitForEvents ? [NSDate distantFuture] : [NSDate distantPast]
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+ if (event) {
+ [NSApp sendEvent:event];
+ }
+ } while (!CocoaAppContext::isTerminated && event);
+}
+
+void Application::doEvents()
+{
+ doEventsImpl(false);
+}
+
+void Application::quit()
+{
+ CocoaAppContext::isTerminated = true;
+}
+
+void Application::dispose()
+{
+ CocoaAppContext::mainWindow = nullptr;
+}
+
+void Application::run(Window* mainWindow, bool waitForEvents)
+{
+ if (mainWindow)
+ {
+ CocoaAppContext::mainWindow = mainWindow;
+ mainWindow->show();
+ }
+ while (!CocoaAppContext::isTerminated)
+ {
+ doEventsImpl(waitForEvents);
+ if (CocoaAppContext::isTerminated)
+ break;
+ if (mainWindow)
+ {
+ mainWindow->events.mainLoop();
+ }
+ }
+}
+
+CocoaPlatformWindow::CocoaPlatformWindow(const WindowDesc& desc)
+{
+ // Create a reference rectangle
+ NSRect rect = NSMakeRect(0.0f, 0.0f, desc.width, desc.height);
+
+ // Allocate window
+ window = [[NSWindow alloc] initWithContentRect:rect
+ styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable
+ backing:NSBackingStoreBuffered
+ defer:NO];
+
+ const NSWindowCollectionBehavior behavior
+ = NSWindowCollectionBehaviorFullScreenPrimary | NSWindowCollectionBehaviorManaged;
+ [window setCollectionBehavior:behavior];
+
+ if (desc.style == WindowStyle::Default)
+ [window setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable];
+ else if (desc.style == WindowStyle::FixedSize)
+ [window setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable];
+
+ // Allocate view
+ rect = [window backingAlignedRect:rect options:NSAlignAllEdgesOutward];
+ view = [[ContentView alloc] initWithPlatformWindow:this];
+ [view setHidden:NO];
+ [view setNeedsDisplay:YES];
+ [view setWantsLayer:YES];
+
+ delegate = [[WindowDelegate alloc] initWithPlatformWindow:this];
+ [window setDelegate:delegate];
+ [window setContentView:view];
+
+ NSString* title = [NSString stringWithUTF8String:desc.title];
+ [window setTitle:title];
+
+ [window center];
+ [window makeKeyAndOrderFront:nil];
+
+ // Setup layer
+ layer = [[CAMetalLayer alloc] init];
+ [view setLayer:layer];
+}
+
+CocoaPlatformWindow::~CocoaPlatformWindow()
+{
+ close();
+}
+
+void CocoaPlatformWindow::setClientSize(uint32_t width, uint32_t height)
+{
+ NSSize size = NSMakeSize(width, height);
+ [window setContentSize:size];
+}
+
+Rect CocoaPlatformWindow::getClientRect()
+{
+ NSRect rect = [window contentRectForFrameRect:[window frame]];
+ return { (int)rect.origin.x, (int)rect.origin.y, (int)rect.size.width, (int)rect.size.height };
+}
+
+void CocoaPlatformWindow::centerScreen()
+{
+ [window center];
+}
+
+void CocoaPlatformWindow::close()
+{
+ [window release];
+ [delegate release];
+ [view release];
+ [layer release];
+
+ window = nil;
+ delegate = nil;
+ view = nil;
+ layer = nil;
+}
+
+bool CocoaPlatformWindow::getFocused()
+{
+ return [window isKeyWindow];
+}
+
+bool CocoaPlatformWindow::getVisible()
+{
+ return [window isVisible];
+}
+
+WindowHandle CocoaPlatformWindow::getNativeHandle()
+{
+ return WindowHandle::fromNSView(view);
+}
+
+void CocoaPlatformWindow::setText(Slang::String text)
+{
+ NSString* title = [NSString stringWithUTF8String:text.begin()];
+ [window setTitle:title];
+}
+
+void CocoaPlatformWindow::show()
+{
+ [window setIsVisible:YES];
+}
+
+void CocoaPlatformWindow::hide()
+{
+ [window setIsVisible:NO];
+}
+
+int CocoaPlatformWindow::getCurrentDpi()
+{
+ // There seems to be no API to get the actual DPI of the screen.
+ return 0;
+}
+
+Window* Application::createWindow(const WindowDesc& desc) { return new CocoaPlatformWindow(desc); }
+
+
+} // namespace platform
+
+#endif // __APPLE__
diff --git a/tools/platform/placeholder/placeholder-window.cpp b/tools/platform/placeholder/placeholder-window.cpp
index a94287d55..ae4f413f8 100644
--- a/tools/platform/placeholder/placeholder-window.cpp
+++ b/tools/platform/placeholder/placeholder-window.cpp
@@ -1,4 +1,4 @@
-#if !defined(_WIN32) && !defined(SLANG_ENABLE_XLIB)
+#if !defined(_WIN32) && !defined(__APPLE__) && !defined(SLANG_ENABLE_XLIB)
#include "../window.h"
diff --git a/tools/platform/window.h b/tools/platform/window.h
index f80e21302..7f78b8af9 100644
--- a/tools/platform/window.h
+++ b/tools/platform/window.h
@@ -97,6 +97,7 @@ struct WindowHandle
{
Unknown,
Win32Handle,
+ NSViewHandle,
XLibHandle,
};
Type type;
@@ -108,6 +109,13 @@ struct WindowHandle
handle.handleValues[0] = (intptr_t)(hwnd);
return handle;
}
+ static WindowHandle fromNSView(void* nsview)
+ {
+ WindowHandle handle = {};
+ handle.type = WindowHandle::Type::NSViewHandle;
+ handle.handleValues[0] = (intptr_t)(nsview);
+ return handle;
+ }
static WindowHandle fromXWindow(void* xdisplay, uint32_t xwindow)
{
WindowHandle handle = {};