From 4fe38195db07e2908b989e0e3361c6717447f954 Mon Sep 17 00:00:00 2001 From: Nils Schmidt Date: Fri, 15 Mar 2024 11:16:52 +0100 Subject: [PATCH] Did some experiments with a vulkan canvas for macosx. --- .../vulkan/swt/PlatformMacOSXVKCanvas.java | 181 ++++++++++++++++++ src/org/lwjgl/vulkan/swt/VKCanvas.java | 3 + .../nschmidt/ldparteditor/VKCanvasTest.java | 112 +++++++++++ 3 files changed, 296 insertions(+) create mode 100644 src-macos/org/lwjgl/vulkan/swt/PlatformMacOSXVKCanvas.java create mode 100644 test/org/nschmidt/ldparteditor/VKCanvasTest.java diff --git a/src-macos/org/lwjgl/vulkan/swt/PlatformMacOSXVKCanvas.java b/src-macos/org/lwjgl/vulkan/swt/PlatformMacOSXVKCanvas.java new file mode 100644 index 000000000..dfdd24364 --- /dev/null +++ b/src-macos/org/lwjgl/vulkan/swt/PlatformMacOSXVKCanvas.java @@ -0,0 +1,181 @@ +package org.lwjgl.vulkan.swt; + +import org.eclipse.swt.SWTException; +import org.eclipse.swt.widgets.Composite; +import org.lwjgl.PointerBuffer; +import org.lwjgl.vulkan.VkAllocationCallbacks; + +import static org.lwjgl.system.MemoryUtil.memAllocPointer; +import static org.lwjgl.vulkan.EXTMetalSurface.*; +import org.lwjgl.vulkan.VkPhysicalDevice; +import static org.lwjgl.vulkan.VK10.*; + +import java.nio.LongBuffer; + +import org.lwjgl.vulkan.VkMetalSurfaceCreateInfoEXT; + +import org.lwjgl.BufferUtils; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.*; +import org.lwjgl.system.libffi.FFICIF; +import org.lwjgl.system.libffi.LibFFI; +import org.lwjgl.system.macosx.MacOSXLibrary; +import org.lwjgl.system.macosx.ObjCRuntime; +import org.lwjgl.vulkan.VkInstance; +import org.lwjgl.vulkan.VkMetalSurfaceCreateInfoEXT; +import org.lwjgl.vulkan.VkPhysicalDevice; + +import javax.swing.*; +import java.awt.*; +import java.nio.ByteBuffer; +import java.nio.LongBuffer; + +import static org.lwjgl.vulkan.EXTMetalSurface.*; +import static org.lwjgl.vulkan.KHRSurface.VK_ERROR_NATIVE_WINDOW_IN_USE_KHR; +import static org.lwjgl.vulkan.VK10.*; + +public class PlatformMacOSXVKCanvas implements PlatformVKCanvas { + + @Override + public int checkStyle(Composite parent, int style) { + return style; + } + + @Override + public void resetStyle(Composite parent) { + // NOP + } + + @Override + public long create(Composite composite, VKData data) { + long metalLayer = composite.getParent().getParent().view.layer().id;//createMTKView(0, 0, 1024, 768); + + final VkMetalSurfaceCreateInfoEXT ci = VkMetalSurfaceCreateInfoEXT.calloc() + .sType(VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT) + .pLayer(PointerBuffer.create(metalLayer, 1)); + final VkAllocationCallbacks cb = null; + final LongBuffer pSurface = LongBuffer.allocate(1); + int err = vkCreateMetalSurfaceEXT(data.instance, ci, cb, pSurface); + long surface = pSurface.get(0); + if (err != VK_SUCCESS) { + throw new SWTException("Calling vkCreateMetalSurfaceEXT failed with error: " + err); //$NON-NLS-1$ + } + return surface; + } + + @Override + public boolean getPhysicalDevicePresentationSupport(VkPhysicalDevice physicalDevice, int queueFamily) { + // TODO Auto-generated method stub + return false; + } + + /** + * Creates the native Metal view. + *

+ * Because {@link JNI} does not provide a method signature for {@code PPDDDDPP}, + * we have to construct a call interface ourselves via {@link LibFFI}. + *

+ * {@code + * id device = MTLCreateSystemDefaultDevice(); + * MTKView *view = [[MTKView alloc] initWithFrame:frame device:device]; // frame is from a GCRectMake(); + * surfaceLayers.layer = view.layer; // jawt platform object + * return view.layer; + * } + * + * @param x x position of the window + * @param y y position of the window + * @param width window width + * @param height window height + * @return pointer to a native window handle + */ + private static long createMTKView(int x, int y, int width, int height) { + try (MemoryStack stack = MemoryStack.stackPush()) { + SharedLibrary metalKit = MacOSXLibrary.create("/System/Library/Frameworks/MetalKit.framework"); //$NON-NLS-1$ + SharedLibrary metal = MacOSXLibrary.create("/System/Library/Frameworks/Metal.framework"); //$NON-NLS-1$ + long objc_msgSend = ObjCRuntime.getLibrary().getFunctionAddress("objc_msgSend"); //$NON-NLS-1$ + metalKit.getFunctionAddress("MTKView"); // loads the MTKView class or something (required, somehow) //$NON-NLS-1$ + + // id device = MTLCreateSystemDefaultDevice(); + long device = JNI.invokeP(metal.getFunctionAddress("MTLCreateSystemDefaultDevice")); //$NON-NLS-1$ + + + PointerBuffer argumentTypes = BufferUtils.createPointerBuffer(7) // 4 arguments, one of them an array of 4 doubles + .put(0, LibFFI.ffi_type_pointer) // MTKView* + .put(1, LibFFI.ffi_type_pointer) // initWithFrame: + .put(2, LibFFI.ffi_type_double) // CGRect + .put(3, LibFFI.ffi_type_double) // CGRect + .put(4, LibFFI.ffi_type_double) // CGRect + .put(5, LibFFI.ffi_type_double) // CGRect + .put(6, LibFFI.ffi_type_pointer); // device* + + // Prepare the call interface + FFICIF cif = FFICIF.malloc(stack); + int status = LibFFI.ffi_prep_cif(cif, LibFFI.FFI_DEFAULT_ABI, LibFFI.ffi_type_pointer, argumentTypes); + if (status != LibFFI.FFI_OK) { + throw new IllegalStateException("ffi_prep_cif failed: " + status); //$NON-NLS-1$ + } + + // An array of pointers that point to the actual argument values. + PointerBuffer arguments = stack.mallocPointer(7); + + // Storage for the actual argument values. + ByteBuffer values = stack.malloc( + Pointer.POINTER_SIZE + // MTKView* + Pointer.POINTER_SIZE + // initWithFrame* + Double.BYTES * 4 + // CGRect (4 doubles) + Pointer.POINTER_SIZE // device* + ); + + // MTKView *view = [MTKView alloc]; + long mtkView = JNI.invokePPP( + ObjCRuntime.objc_getClass("MTKView"), //$NON-NLS-1$ + ObjCRuntime.sel_getUid("alloc"), //$NON-NLS-1$ + objc_msgSend); + + // Set up the argument buffers by inserting pointers + + // MTKView* + arguments.put(MemoryUtil.memAddress(values)); + PointerBuffer.put(values, mtkView); + + // initWithFrame* + arguments.put(MemoryUtil.memAddress(values)); + PointerBuffer.put(values, ObjCRuntime.sel_getUid("initWithFrame:")); //$NON-NLS-1$ + + // frame + arguments.put(MemoryUtil.memAddress(values)); + values.putDouble(x); + arguments.put(MemoryUtil.memAddress(values)); + values.putDouble(y); + arguments.put(MemoryUtil.memAddress(values)); + values.putDouble(width); + arguments.put(MemoryUtil.memAddress(values)); + values.putDouble(height); + + // device* + arguments.put(MemoryUtil.memAddress(values)); + values.putLong(device); + + arguments.flip(); + values.flip(); + + // [view initWithFrame:rect device:device]; + // Returns itself, we just need to know if it's NULL + LongBuffer pMTKView = stack.mallocLong(1); + LibFFI.ffi_call(cif, objc_msgSend, MemoryUtil.memByteBuffer(pMTKView), arguments); + if (pMTKView.get(0) == MemoryUtil.NULL) { + throw new IllegalStateException("[MTKView initWithFrame:device:] returned null."); //$NON-NLS-1$ + } + + + // layer = view.layer; + long layer = JNI.invokePPP(mtkView, + ObjCRuntime.sel_getUid("layer"), //$NON-NLS-1$ + objc_msgSend); + + // return layer; + return layer; + } + } + +} diff --git a/src/org/lwjgl/vulkan/swt/VKCanvas.java b/src/org/lwjgl/vulkan/swt/VKCanvas.java index ed83e83fa..25cbb69ff 100644 --- a/src/org/lwjgl/vulkan/swt/VKCanvas.java +++ b/src/org/lwjgl/vulkan/swt/VKCanvas.java @@ -37,6 +37,9 @@ public class VKCanvas extends Canvas { static { String platformClassName; switch (Platform.get()) { + case MACOSX: + platformClassName = "org.lwjgl.vulkan.swt.PlatformMacOSXVKCanvas"; //$NON-NLS-1$ + break; case WINDOWS: platformClassName = "org.lwjgl.vulkan.swt.PlatformWin32VKCanvas"; //$NON-NLS-1$ break; diff --git a/test/org/nschmidt/ldparteditor/VKCanvasTest.java b/test/org/nschmidt/ldparteditor/VKCanvasTest.java new file mode 100644 index 000000000..2370dbf03 --- /dev/null +++ b/test/org/nschmidt/ldparteditor/VKCanvasTest.java @@ -0,0 +1,112 @@ +/* MIT - License + +Copyright (c) 2012 - this year, Nils Schmidt + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +package org.nschmidt.ldparteditor; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.junit.Test; +import static org.lwjgl.vulkan.VK10.*; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.vulkan.VkApplicationInfo; +import org.lwjgl.vulkan.VkInstance; +import org.lwjgl.vulkan.VkInstanceCreateInfo; +import org.lwjgl.vulkan.swt.VKCanvas; +import org.lwjgl.vulkan.swt.VKData; +import java.nio.ByteBuffer; + +import static org.lwjgl.vulkan.VK10.VK_ERROR_EXTENSION_NOT_PRESENT; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_EXT_DEBUG_UTILS_EXTENSION_NAME; +import static org.lwjgl.vulkan.EXTDebugReport.VK_EXT_DEBUG_REPORT_EXTENSION_NAME; + +import static org.lwjgl.vulkan.KHRSurface.VK_KHR_SURFACE_EXTENSION_NAME; +import static org.lwjgl.vulkan.EXTMetalSurface.VK_EXT_METAL_SURFACE_EXTENSION_NAME; +import static org.lwjgl.system.MemoryUtil.memAllocPointer; +import static org.lwjgl.system.MemoryUtil.memFree; +import static org.lwjgl.system.MemoryUtil.memUTF8; + +@SuppressWarnings("java:S5960") +public class VKCanvasTest { + + @Test + public void testInstanceCreation() { + final Display display = new Display(); + final Shell sh = new Shell(display); + sh.setSize(1024, 768); + sh.open(); + sh.setActive(); + + int i = 0; + while (!sh.isDisposed() && i < 5) { + if (!display.readAndDispatch()) + { display.sleep();} + try { + Thread.sleep(1000L); + } catch (InterruptedException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + i++; + } + + final Composite cmp = sh; + final VKData data = new VKData(); + + ByteBuffer VK_EXT_DEBUG_REPORT_EXTENSION = memUTF8(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); + ByteBuffer VK_EXT_DEBUG_UTILS_EXTENSION = memUTF8(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + ByteBuffer VK_KHR_SURFACE_EXTENSION = memUTF8(VK_KHR_SURFACE_EXTENSION_NAME); + ByteBuffer VK_EXT_METAL_SURFACE_EXTENSION = memUTF8(VK_EXT_METAL_SURFACE_EXTENSION_NAME); + + PointerBuffer ppEnabledExtensionNames = memAllocPointer(4); + ppEnabledExtensionNames.put(VK_EXT_DEBUG_REPORT_EXTENSION); + ppEnabledExtensionNames.put(VK_EXT_DEBUG_UTILS_EXTENSION); + ppEnabledExtensionNames.put(VK_KHR_SURFACE_EXTENSION); + ppEnabledExtensionNames.put(VK_EXT_METAL_SURFACE_EXTENSION); + ppEnabledExtensionNames.flip(); + + VkApplicationInfo appInfo = VkApplicationInfo.calloc() + .sType(VK_STRUCTURE_TYPE_APPLICATION_INFO) + .pApplicationName(memUTF8("Vulkan Demo")) //$NON-NLS-1$ + .pEngineName(memUTF8("LDPE ENGINE")) //$NON-NLS-1$ + .apiVersion(VK_MAKE_VERSION(1, 1, 0)); + + VkInstanceCreateInfo pCreateInfo = VkInstanceCreateInfo.calloc() + .sType(VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO) + .pNext(0) + .pApplicationInfo(appInfo) + .ppEnabledExtensionNames(ppEnabledExtensionNames); + PointerBuffer pInstance = memAllocPointer(1); + + int err = vkCreateInstance(pCreateInfo, null, pInstance); + + if (err != VK_SUCCESS) { + throw new AssertionError("Failed to create VkInstance: " + err); //$NON-NLS-1$ + } + + VkInstance handle = new VkInstance(pInstance.get(0), pCreateInfo); + + pCreateInfo.free(); + memFree(pInstance); + memFree(appInfo.pApplicationName()); + memFree(appInfo.pEngineName()); + appInfo.free(); + + data.instance = handle; + final VKCanvas cut = new VKCanvas(new Composite(cmp, SWT.NONE), SWT.NONE, data); + } +}