From: Not Zed <notzed@gmail.com>
Date: Sun, 25 Sep 2022 12:41:43 +0000 (+0930)
Subject: Update to OpenJDK 19 java.lang.foriegn.
X-Git-Url: https://code.zedzone.au/cvs?a=commitdiff_plain;p=vulkanz

Update to OpenJDK 19 java.lang.foriegn.
Advance tutorial to chapter 29.
---

diff --git a/nbproject/project.properties b/nbproject/project.properties
index 484a1fa..b0c5f0d 100644
--- a/nbproject/project.properties
+++ b/nbproject/project.properties
@@ -40,7 +40,7 @@ includes=**
 jar.compress=false
 javac.classpath=
 # Space-separated list of extra javac options
-javac.compilerargs=
+javac.compilerargs=--enable-preview
 javac.deprecation=false
 javac.external.vm=false
 javac.modulepath=\
@@ -48,8 +48,8 @@ javac.modulepath=\
 javac.processormodulepath=
 javac.processorpath=\
     ${javac.classpath}
-javac.source=18
-javac.target=18
+javac.source=19
+javac.target=19
 javac.test.classpath=\
     ${javac.classpath}
 javac.test.modulepath=\
@@ -83,7 +83,7 @@ run.classpath=
 # Space-separated list of JVM arguments used when running the project.
 # You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
 # To set system properties for unit tests define test-sys-prop.name=value:
-run.jvmargs=--enable-native-access=notzed.vulkan,notzed.nativez,notzed.xlib
+run.jvmargs=--enable-native-access=notzed.vulkan,notzed.nativez,notzed.xlib --enable-preview
 run.modulepath=\
     ${javac.modulepath}:\
     ${build.modules.dir}
diff --git a/src/notzed.display/classes/au/notzed/display/Display.java b/src/notzed.display/classes/au/notzed/display/Display.java
index 55806b8..e418279 100644
--- a/src/notzed.display/classes/au/notzed/display/Display.java
+++ b/src/notzed.display/classes/au/notzed/display/Display.java
@@ -1,12 +1,6 @@
 
 package au.notzed.display;
 
-import jdk.incubator.foreign.*;
-import au.notzed.nativez.*;
-
-import vulkan.*;
-import static vulkan.Vulkan.*;
-
 import xlib.*;
 import static xlib.XLib.*;
 
diff --git a/src/notzed.display/classes/au/notzed/display/Window.java b/src/notzed.display/classes/au/notzed/display/Window.java
index ef1106b..b2a9755 100644
--- a/src/notzed.display/classes/au/notzed/display/Window.java
+++ b/src/notzed.display/classes/au/notzed/display/Window.java
@@ -1,17 +1,16 @@
 package au.notzed.display;
 
-import jdk.incubator.foreign.*;
 import au.notzed.nativez.*;
+import java.lang.foreign.MemorySession;
 
 import vulkan.*;
-import static vulkan.Vulkan.*;
 
 import xlib.*;
 import static xlib.XLib.*;
 
 public abstract class Window {
 
-	public abstract VkSurfaceKHR createVulkanSurface(VkInstance instance, ResourceScope scope);
+	public abstract VkSurfaceKHR createVulkanSurface(VkInstance instance, MemorySession scope);
 
 	public abstract Event nextEvent(boolean blocking);
 
@@ -126,7 +125,7 @@ public abstract class Window {
 		}
 
 		@Override
-		public VkSurfaceKHR createVulkanSurface(VkInstance instance, ResourceScope scope) {
+		public VkSurfaceKHR createVulkanSurface(VkInstance instance, MemorySession scope) {
 			try ( Frame frame = Frame.frame()) {
 				VkXlibSurfaceCreateInfoKHR surfaceinfo = VkXlibSurfaceCreateInfoKHR.create(
 					0,
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/Demo.java b/src/notzed.vulkan.test/classes/vulkan/test/Demo.java
index ff3cfc5..a803f13 100644
--- a/src/notzed.vulkan.test/classes/vulkan/test/Demo.java
+++ b/src/notzed.vulkan.test/classes/vulkan/test/Demo.java
@@ -24,12 +24,7 @@ import au.notzed.nativez.Memory;
 import java.util.HashSet;
 import java.util.List;
 import java.util.stream.Stream;
-import jdk.incubator.foreign.GroupLayout;
-import jdk.incubator.foreign.MemoryLayout;
-import jdk.incubator.foreign.MemorySegment;
-import jdk.incubator.foreign.ResourceScope;
-import jdk.incubator.foreign.SegmentAllocator;
-import jdk.incubator.foreign.ValueLayout;
+import java.lang.foreign.*;
 import vulkan.PFN_vkDebugUtilsMessengerCallbackEXT;
 import vulkan.VkApplicationInfo;
 import vulkan.VkAttachmentDescription;
@@ -44,6 +39,13 @@ import vulkan.VkCommandPool;
 import vulkan.VkCommandPoolCreateInfo;
 import vulkan.VkDebugUtilsMessengerCreateInfoEXT;
 import vulkan.VkDebugUtilsMessengerEXT;
+import vulkan.VkDescriptorBufferInfo;
+import vulkan.VkDescriptorPool;
+import vulkan.VkDescriptorPoolCreateInfo;
+import vulkan.VkDescriptorPoolSize;
+import vulkan.VkDescriptorSet;
+import vulkan.VkDescriptorSetAllocateInfo;
+import vulkan.VkDescriptorSetLayout;
 import vulkan.VkDevice;
 import vulkan.VkDeviceCreateInfo;
 import vulkan.VkDeviceMemory;
@@ -95,6 +97,7 @@ import vulkan.VkSwapchainCreateInfoKHR;
 import vulkan.VkSwapchainKHR;
 import vulkan.VkVertexInputAttributeDescription;
 import vulkan.VkVertexInputBindingDescription;
+import vulkan.VkWriteDescriptorSet;
 import vulkan.Vulkan;
 import static vulkan.Vulkan.*;
 
@@ -110,7 +113,7 @@ public class Demo {
 	public final static int QUEUE_PRESENT = 0x80;
 	public final static int QUEUE_MASK = QUEUE_GRAPHICS | QUEUE_COMPUTE | QUEUE_TRANSFER;
 
-	public static VkInstance createInstance(int apiVersion, String[] layers, String[] extensions, ResourceScope scope) {
+	public static VkInstance createInstance(int apiVersion, String[] layers, String[] extensions, MemorySession scope) {
 		try ( Frame frame = Frame.frame()) {
 			VkInstanceCreateInfo info = VkInstanceCreateInfo.create(
 				0,
@@ -124,7 +127,7 @@ public class Demo {
 		}
 	}
 
-	public static VkDebugUtilsMessengerEXT createLogger(VkInstance instance, ResourceScope scope) throws Exception {
+	public static VkDebugUtilsMessengerEXT createLogger(VkInstance instance, MemorySession scope) throws Exception {
 		try ( Frame frame = Frame.frame()) {
 			var cb = PFN_vkDebugUtilsMessengerCallbackEXT.upcall((severity, flags, data) -> {
 				System.out.printf("Debug: %d: %s\n", severity, data.getMessage());
@@ -236,21 +239,21 @@ public class Demo {
 			device.vkDestroyDevice();
 		}
 
-		public VkSemaphore createSemaphore(ResourceScope scope) {
+		public VkSemaphore createSemaphore(MemorySession scope) {
 			try ( Frame frame = Frame.frame()) {
 				VkSemaphoreCreateInfo info = VkSemaphoreCreateInfo.create(frame);
 				return device.vkCreateSemaphore(info, scope);
 			}
 		}
 
-		public VkFence createFence(ResourceScope scope) {
+		public VkFence createFence(MemorySession scope) {
 			try ( Frame frame = Frame.frame()) {
 				VkFenceCreateInfo info = VkFenceCreateInfo.create(frame);
 				return device.vkCreateFence(info, scope);
 			}
 		}
 
-		public HandleArray<VkSemaphore> createSemaphores(int count, SegmentAllocator alloc, ResourceScope scope) {
+		public HandleArray<VkSemaphore> createSemaphores(int count, SegmentAllocator alloc, MemorySession scope) {
 			HandleArray<VkSemaphore> list = VkSemaphore.createArray(count, alloc, scope);
 			try ( Frame frame = Frame.frame()) {
 				VkSemaphoreCreateInfo info = VkSemaphoreCreateInfo.create(frame);
@@ -261,7 +264,7 @@ public class Demo {
 			return list;
 		}
 
-		public HandleArray<VkFence> createFences(int count, int flags, SegmentAllocator alloc, ResourceScope scope) {
+		public HandleArray<VkFence> createFences(int count, int flags, SegmentAllocator alloc, MemorySession scope) {
 			HandleArray<VkFence> list = VkFence.createArray(count, alloc, scope);
 			try ( Frame frame = Frame.frame()) {
 				VkFenceCreateInfo info = VkFenceCreateInfo.create(flags, frame);
@@ -285,7 +288,7 @@ public class Demo {
 	}
 
 	// TODO: alloc?
-	public static DemoDevice createDevice(VkInstance instance, VkPhysicalDeviceFeatures features, VkSurfaceKHR surface, int[] queueFlags, String[] extensions, ResourceScope scope) {
+	public static DemoDevice createDevice(VkInstance instance, VkPhysicalDeviceFeatures features, VkSurfaceKHR surface, int[] queueFlags, String[] extensions, MemorySession scope) {
 		try ( Frame frame = Frame.frame()) {
 			HandleArray<VkPhysicalDevice> devs = instance.vkEnumeratePhysicalDevices(frame, scope);
 			VkPhysicalDeviceProperties dp = VkPhysicalDeviceProperties.create(frame);
@@ -425,7 +428,7 @@ public class Demo {
 
 	}
 
-	static DemoChain createSwapchain(DemoDevice dd, ColourFormat prefFormat, int prefPresentMode, int pixWidth, int pixHeight, SegmentAllocator alloc, ResourceScope scope) {
+	static DemoChain createSwapchain(DemoDevice dd, ColourFormat prefFormat, int prefPresentMode, int pixWidth, int pixHeight, SegmentAllocator alloc, MemorySession scope) {
 		try ( Frame frame = Frame.frame()) {
 			ColourFormat format = selectColourFormat(
 				dd.dev.vkGetPhysicalDeviceSurfaceFormatsKHR(dd.surface, frame),
@@ -520,7 +523,7 @@ public class Demo {
 	    + specialisation info to initialise constants per-pipeline shader stage init
 
 	 */
-	static VkRenderPass dumbRenderPass(VkDevice device, int format, ResourceScope scope) {
+	static VkRenderPass dumbRenderPass(VkDevice device, int format, MemorySession scope) {
 		try ( Frame frame = Frame.frame()) {
 			VkAttachmentDescription colourAttachment = VkAttachmentDescription.create(
 				0,
@@ -557,7 +560,7 @@ public class Demo {
 		}
 	}
 
-	static VkShaderModule createShaderModule(VkDevice device, IntArray spirv, ResourceScope scope) {
+	static VkShaderModule createShaderModule(VkDevice device, IntArray spirv, MemorySession scope) {
 		try ( Frame frame = Frame.frame()) {
 			VkShaderModuleCreateInfo vsInfo = VkShaderModuleCreateInfo.create(
 				0,
@@ -568,14 +571,14 @@ public class Demo {
 		}
 	}
 
-	static VkPipelineLayout dumbPipelineLayout(VkDevice device, ResourceScope scope) {
+	static VkPipelineLayout dumbPipelineLayout(VkDevice device, MemorySession scope) {
 		try ( Frame frame = Frame.frame()) {
 			VkPipelineLayoutCreateInfo layoutInfo = VkPipelineLayoutCreateInfo.create(0, 0, null, 0, null, frame);
 			return device.vkCreatePipelineLayout(layoutInfo, scope);
 		}
 	}
 
-	static VkPipeline dumbPipeline(VkDevice device, VkShaderModule vertex, VkShaderModule fragment, VkPipelineLayout layout, VkRenderPass renderPass, ResourceScope scope) {
+	static VkPipeline dumbPipeline(VkDevice device, VkShaderModule vertex, VkShaderModule fragment, VkPipelineLayout layout, VkRenderPass renderPass, MemorySession scope) {
 		try ( Frame frame = Frame.frame()) {
 			VkPipelineShaderStageCreateInfo shader = VkPipelineShaderStageCreateInfo.createArray(2, frame);
 
@@ -662,7 +665,7 @@ public class Demo {
 		}
 	}
 
-	public static HandleArray<VkFramebuffer> createFramebuffers(DemoDevice device, DemoChain chain, SegmentAllocator alloc, ResourceScope scope) {
+	public static HandleArray<VkFramebuffer> createFramebuffers(DemoDevice device, DemoChain chain, SegmentAllocator alloc, MemorySession scope) {
 		try ( Frame frame = Frame.frame()) {
 			var frameBuffers = VkFramebuffer.createArray(chain.views.length(), alloc);
 			HandleArray<VkImageView> attachments = VkImageView.createArray(1, frame);
@@ -682,7 +685,7 @@ public class Demo {
 		}
 	}
 
-	public static HandleArray<VkCommandBuffer> createCommandBuffers(DemoDevice device, int appIndex, int count, SegmentAllocator alloc, ResourceScope scope) {
+	public static HandleArray<VkCommandBuffer> createCommandBuffers(DemoDevice device, int appIndex, int count, SegmentAllocator alloc, MemorySession scope) {
 		try ( Frame frame = Frame.frame()) {
 			VkCommandBufferAllocateInfo bufferInfo = VkCommandBufferAllocateInfo.create(
 				device.pools[appIndex],
@@ -720,7 +723,7 @@ public class Demo {
 	}
 
 	// TODO: memory allocator?  - limited memory blocks?
-	public static Buffer createBuffer(DemoDevice device, long size, int usage, int props, int[] sharedFamilies, ResourceScope scope) {
+	public static Buffer createBuffer(DemoDevice device, long size, int usage, int props, int[] sharedFamilies, MemorySession scope) {
 		try ( Frame frame = Frame.frame()) {
 			IntArray families = sharedFamilies != null ? IntArray.create(frame, sharedFamilies) : null;
 			VkBufferCreateInfo create = VkBufferCreateInfo.create(0, size, usage, VK_SHARING_MODE_CONCURRENT, sharedFamilies != null ? sharedFamilies.length : 0, families, frame);
@@ -744,7 +747,7 @@ public class Demo {
 		}
 	}
 
-	public static Buffer createBuffer(DemoDevice device, MemorySegment src, int usage, int props, int[] sharedFamilies, ResourceScope scope) {
+	public static Buffer createBuffer(DemoDevice device, MemorySegment src, int usage, int props, int[] sharedFamilies, MemorySession scope) {
 		try ( Frame frame = Frame.frame()) {
 			Buffer target = createBuffer(device, src.byteSize(), usage | Vulkan.VK_BUFFER_USAGE_TRANSFER_DST_BIT, props, sharedFamilies, scope);
 			Buffer source = createBuffer(device, src.byteSize(), Vulkan.VK_BUFFER_USAGE_TRANSFER_SRC_BIT, Vulkan.VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, sharedFamilies, frame.scope());
@@ -784,6 +787,67 @@ public class Demo {
 
 	}
 
+	public static record UniformBuffers(
+		VkDescriptorPool descriptorPool,
+		HandleArray<VkDescriptorSet> descriptorSets,
+		Buffer[] uniformBuffers) {
+
+		public void close(VkDevice device) {
+			Stream.of(uniformBuffers).forEach(d -> d.close(device));
+			device.vkDestroyDescriptorPool(descriptorPool);
+		}
+
+	}
+
+	static UniformBuffers createUniformBuffers(DemoDevice device, VkDescriptorSetLayout descriptorSetLayout, MemoryLayout uniformLayout, int frameCount, SegmentAllocator alloc, MemorySession scope) {
+		try ( Frame frame = Frame.frame()) {
+			// uniform buffers
+			Demo.Buffer[] uniformBuffers = new Demo.Buffer[frameCount];
+			for (int i = 0; i < uniformBuffers.length; i++) {
+				uniformBuffers[i] = Demo.createBuffer(device, uniformLayout.byteSize(),
+					Vulkan.VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+					Vulkan.VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | Vulkan.VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+					device.queueFamilies(), scope);
+			}
+
+			// descriptor pools for above
+			VkDescriptorPoolSize poolSize = VkDescriptorPoolSize.create(
+				VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+				frameCount,
+				frame);
+
+			VkDescriptorPoolCreateInfo poolInfo = VkDescriptorPoolCreateInfo.create(
+				0,
+				frameCount,
+				1, poolSize,
+				frame);
+			VkDescriptorPool descriptorPool;
+			descriptorPool = device.device().vkCreateDescriptorPool(poolInfo, scope);
+
+			// sets
+			var poolSetLayout = VkDescriptorSetLayout.createArray(frameCount, frame);
+			for (int i = 0; i < frameCount; i++)
+				poolSetLayout.setAtIndex(i, descriptorSetLayout);
+			VkDescriptorSetAllocateInfo allocInfo = VkDescriptorSetAllocateInfo.create(
+				descriptorPool,
+				frameCount, poolSetLayout,
+				frame);
+
+			HandleArray<VkDescriptorSet> descriptorSets = device.device().vkAllocateDescriptorSets(allocInfo, alloc, scope);
+			for (int i = 0; i < frameCount; i++) {
+				VkDescriptorBufferInfo bufferInfo = VkDescriptorBufferInfo.create(uniformBuffers[i].buffer(), 0, uniformLayout.byteSize(), frame);
+				VkWriteDescriptorSet descriptorWrite = VkWriteDescriptorSet.create(
+					descriptorSets.get(i),
+					0, 0,
+					1, Vulkan.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+					null, bufferInfo, null, frame);
+				device.device().vkUpdateDescriptorSets(1, descriptorWrite, 0, null);
+			}
+
+			return new UniformBuffers(descriptorPool, descriptorSets, uniformBuffers);
+		}
+	}
+
 	// not worth it, its simple enough
 	static VkVertexInputAttributeDescription createInputAttributes(SegmentAllocator alloc, int binding, int... loc) {
 		int len = loc.length / 3;
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/TestCube.java b/src/notzed.vulkan.test/classes/vulkan/test/TestCube.java
index f68a177..b883958 100644
--- a/src/notzed.vulkan.test/classes/vulkan/test/TestCube.java
+++ b/src/notzed.vulkan.test/classes/vulkan/test/TestCube.java
@@ -33,7 +33,7 @@ THE SOFTWARE.
 package vulkan.test;
 
 import au.notzed.display.Display;
-import jdk.incubator.foreign.*;
+import java.lang.foreign.*;
 import au.notzed.nativez.*;
 
 import vulkan.*;
@@ -49,7 +49,7 @@ public class TestCube {
 	final static int NUM_SAMPLES = VK_SAMPLE_COUNT_1_BIT;
 	final static int NUM_DESCRIPTOR_SETS = 1;
 
-	ResourceScope scope = ResourceScope.newSharedScope();
+	MemorySession scope = MemorySession.openShared();
 
 	int width = 800;
 	int height = 800;
@@ -758,7 +758,8 @@ public class TestCube {
 
 		/* Queue the command buffer for execution */
  /* FIXME: frame shoudl provide or take explicit scope */
-		try ( ResourceScope scope = ResourceScope.newConfinedScope();  Frame frame = Frame.frame()) {
+		try ( MemorySession scope = MemorySession.openConfined();
+			 Frame frame = Frame.frame()) {
 			IntArray pipe_stage_flags = IntArray.create(frame, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
 			VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(0, frame);
 			HandleArray<VkFence> fences = VkFence.createArray(1, frame);
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/TestMandelbrot.java b/src/notzed.vulkan.test/classes/vulkan/test/TestMandelbrot.java
index cc139fb..5e42462 100755
--- a/src/notzed.vulkan.test/classes/vulkan/test/TestMandelbrot.java
+++ b/src/notzed.vulkan.test/classes/vulkan/test/TestMandelbrot.java
@@ -45,7 +45,7 @@ import java.awt.Toolkit;
 import java.awt.image.MemoryImageSource;
 import javax.swing.JFrame;
 import javax.swing.JPanel;
-import jdk.incubator.foreign.*;
+import java.lang.foreign.*;
 import au.notzed.nativez.*;
 import java.util.ArrayList;
 import java.util.List;
@@ -56,7 +56,7 @@ import static vulkan.Vulkan.*;
 public class TestMandelbrot {
 
 	static final boolean debug = true;
-	ResourceScope scope = ResourceScope.newSharedScope();
+	MemorySession scope = MemorySession.openShared();
 
 	int WIDTH = 1920 * 1;
 	int HEIGHT = 1080 * 1;
@@ -456,7 +456,7 @@ public class TestMandelbrot {
 	 * Accesses the gpu buffer, converts it to RGB byte, and saves it as a pam file.
 	 */
 	void save_result() throws Exception {
-		try ( ResourceScope scope = ResourceScope.newConfinedScope()) {
+		try ( MemorySession scope = MemorySession.openConfined()) {
 			MemorySegment mem = device.vkMapMemory(dst.memory(), 0, dstBufferSize, 0, scope);
 			byte[] pixels = new byte[WIDTH * HEIGHT * 3];
 
@@ -475,7 +475,7 @@ public class TestMandelbrot {
 	}
 
 	void show_result() throws Exception {
-		try ( ResourceScope scope = ResourceScope.newConfinedScope()) {
+		try ( MemorySession scope = MemorySession.openConfined()) {
 			MemorySegment mem = device.vkMapMemory(dst.memory(), 0, dstBufferSize, 0, scope);
 			int[] pixels = new int[WIDTH * HEIGHT];
 
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/TestSDF.java b/src/notzed.vulkan.test/classes/vulkan/test/TestSDF.java
index c292253..3e56451 100644
--- a/src/notzed.vulkan.test/classes/vulkan/test/TestSDF.java
+++ b/src/notzed.vulkan.test/classes/vulkan/test/TestSDF.java
@@ -35,7 +35,7 @@ package vulkan.test;
 import au.notzed.display.Display;
 import au.notzed.display.Event;
 import au.notzed.display.Window;
-import jdk.incubator.foreign.*;
+import java.lang.foreign.*;
 import au.notzed.nativez.*;
 
 import vulkan.*;
@@ -50,7 +50,7 @@ public class TestSDF {
 	final static int NUM_SAMPLES = VK_SAMPLE_COUNT_1_BIT;
 	final static int NUM_DESCRIPTOR_SETS = 1;
 
-	ResourceScope scope = ResourceScope.newSharedScope();
+	MemorySession scope = MemorySession.openShared();
 
 	int width = 800;
 	int height = 800;
@@ -772,7 +772,8 @@ public class TestSDF {
 
 		/* Queue the command buffer for execution */
  /* FIXME: frame shoudl provide or take explicit scope */
-		try ( ResourceScope scope = ResourceScope.newConfinedScope();  Frame frame = Frame.frame()) {
+		try ( MemorySession scope = MemorySession.openConfined();
+			 Frame frame = Frame.frame()) {
 			IntArray pipe_stage_flags = IntArray.create(frame, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
 			VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(0, frame);
 			HandleArray<VkFence> fences = VkFence.createArray(1, frame);
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/Tutorial.java b/src/notzed.vulkan.test/classes/vulkan/test/Tutorial.java
index c955de3..afc3e79 100644
--- a/src/notzed.vulkan.test/classes/vulkan/test/Tutorial.java
+++ b/src/notzed.vulkan.test/classes/vulkan/test/Tutorial.java
@@ -1,6 +1,8 @@
 /*
 	Going through the Vulkan Tutorial.
-*/
+
+	Chapter 28-29, Descriptor Layout and Buffer - Descriptor Pool Sets
+ */
 package vulkan.test;
 
 import au.notzed.display.Display;
@@ -11,17 +13,20 @@ import au.notzed.nativez.Frame;
 import au.notzed.nativez.HandleArray;
 import au.notzed.nativez.IntArray;
 import au.notzed.nativez.LongArray;
+import au.notzed.nativez.Memory;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Stream;
-import jdk.incubator.foreign.MemorySegment;
-import jdk.incubator.foreign.ResourceScope;
-import jdk.incubator.foreign.SegmentAllocator;
+import java.lang.foreign.*;
+import java.lang.invoke.MethodHandle;
 import vulkan.VkBuffer;
 import vulkan.VkClearValue;
 import vulkan.VkCommandBuffer;
 import vulkan.VkCommandBufferBeginInfo;
+import vulkan.VkDescriptorSetLayout;
+import vulkan.VkDescriptorSetLayoutBinding;
+import vulkan.VkDescriptorSetLayoutCreateInfo;
 import vulkan.VkDevice;
 import vulkan.VkFence;
 import vulkan.VkFramebuffer;
@@ -29,6 +34,7 @@ import vulkan.VkInstance;
 import vulkan.VkPhysicalDeviceFeatures;
 import vulkan.VkPipeline;
 import vulkan.VkPipelineLayout;
+import vulkan.VkPipelineLayoutCreateInfo;
 import vulkan.VkPresentInfoKHR;
 import vulkan.VkRect2D;
 import vulkan.VkRenderPassBeginInfo;
@@ -75,6 +81,40 @@ public class Tutorial {
 		0, 1, 2, 2, 3, 0
 	};
 
+	static final class UniformBufferObject {
+
+		final MemorySegment segment;
+
+		UniformBufferObject(MemorySegment segment) {
+			this.segment = segment;
+		}
+
+		public static UniformBufferObject create(SegmentAllocator alloc) {
+			return new UniformBufferObject(alloc.allocate(LAYOUT));
+		}
+
+		static final MemoryLayout LAYOUT = MemoryLayout.structLayout(
+			mat4.LAYOUT.withName("model"),
+			mat4.LAYOUT.withName("view"),
+			mat4.LAYOUT.withName("proj"));
+
+		final static MethodHandle model$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("model"));
+		final static MethodHandle view$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("view"));
+		final static MethodHandle proj$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("proj"));
+
+		public MemorySegment model() {
+			return Memory.slice(segment, model$SH);
+		}
+
+		public MemorySegment view() {
+			return Memory.slice(segment, view$SH);
+		}
+
+		public MemorySegment proj() {
+			return Memory.slice(segment, proj$SH);
+		}
+	}
+
 	public static record RenderInfo(
 		Demo.DemoDevice device,
 		Demo.DemoChain chain,
@@ -82,8 +122,9 @@ public class Tutorial {
 		VkPipelineLayout pipelineLayout,
 		HandleArray<VkCommandBuffer> commandBuffers,
 		HandleArray<VkFramebuffer> frameBuffers,
+		Demo.UniformBuffers uniformBuffers,
 		Runnable[] resources,
-		ResourceScope scope) {
+		MemorySession scope) {
 
 		public void close() {
 			VkDevice d = device.device();
@@ -92,6 +133,8 @@ public class Tutorial {
 			d.vkDestroyPipeline(pipeline);
 			d.vkDestroyPipelineLayout(pipelineLayout);
 
+			uniformBuffers.close(d);
+
 			for (int i = resources.length - 1; i >= 0; i--)
 				resources[i].run();
 
@@ -104,47 +147,8 @@ public class Tutorial {
 		}
 	}
 
-	static void rebuildCommandBuffersx(RenderInfo info, int extWidth, int extHeight) {
-		Demo.DemoChain chain = info.chain();
-		HandleArray<VkCommandBuffer> commandBuffers = info.commandBuffers();
-		HandleArray<VkFramebuffer> frameBuffers = info.frameBuffers();
-		VkPipeline pipeline = info.pipeline;
-
-		Demo.resetCommandBuffers(info.device, 0, commandBuffers);
-
-		try ( Frame frame = Frame.frame()) {
-			VkCommandBufferBeginInfo begin = VkCommandBufferBeginInfo.create(0, null, frame);
-			VkClearValue clear = VkClearValue.createColour(0, 0, 0, 1, frame);
-			VkRenderPassBeginInfo render = VkRenderPassBeginInfo.create(
-				chain.renderPass(), null,
-				0, 0, extWidth, extHeight,
-				1, clear,
-				frame);
-			VkViewport viewports = VkViewport.create(0, 0, extWidth, extHeight, 0, 1, frame);
-			VkRect2D scissors = VkRect2D.create(0, 0, extWidth, extHeight, frame);
-
-			for (int i = 0; i < commandBuffers.size(); i++) {
-				VkCommandBuffer cmd = commandBuffers.get(i);
-
-				render.setFramebuffer(frameBuffers.get(i));
-
-				cmd.vkBeginCommandBuffer(begin);
-				cmd.vkCmdBeginRenderPass(render, VK_SUBPASS_CONTENTS_INLINE);   // SECONDARY... for sub-rendering?
-
-				cmd.vkCmdSetViewport(0, 1, viewports);
-				cmd.vkCmdSetScissor(0, 1, scissors);
-
-				cmd.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
-				cmd.vkCmdDraw(3, 1, 0, 0);
-
-				cmd.vkCmdEndRenderPass();
-				cmd.vkEndCommandBuffer();
-			}
-		}
-	}
-
 	static RenderInfo createRenderInfo(Demo.DemoDevice device, int pixWidth, int pixHeight, VkShaderModule vertex, VkShaderModule fragment) {
-		ResourceScope scope = ResourceScope.newSharedScope();
+		MemorySession scope = MemorySession.openShared();
 		try ( Frame frame = Frame.frame()) {
 			SegmentAllocator alloc = SegmentAllocator.newNativeArena(scope);
 			List<Runnable> cleanup = new ArrayList<>();
@@ -156,12 +160,22 @@ public class Tutorial {
 				VK_PRESENT_MODE_FIFO_KHR,
 				pixWidth, pixHeight, alloc, scope);
 
-			VkPipelineLayout layout = Demo.dumbPipelineLayout(device.device(), scope);
+			int frameCount = chain.views().size();
+
+			// or is this global scope?
+			VkDescriptorSetLayout descriptorSetLayout = createDescriptorSetLayout(device, scope);
+
+			var setLayouts = VkDescriptorSetLayout.createArray(1, frame);
+			setLayouts.setAtIndex(0, descriptorSetLayout);
+			VkPipelineLayoutCreateInfo layoutInfo = VkPipelineLayoutCreateInfo.create(0, 1, setLayouts, 0, null, frame);
+			VkPipelineLayout layout = device.device().vkCreatePipelineLayout(layoutInfo, scope);
 			VkPipeline pipeline = Demo.dumbPipeline(device.device(), vertex, fragment, layout, chain.renderPass(), scope);
 
 			// could move to chain?
 			HandleArray<VkFramebuffer> frameBuffers = Demo.createFramebuffers(device, chain, alloc, scope);
-			HandleArray<VkCommandBuffer> commandBuffers = Demo.createCommandBuffers(device, 0, frameBuffers.size(), alloc, scope);
+			HandleArray<VkCommandBuffer> commandBuffers = Demo.createCommandBuffers(device, 0, frameCount, alloc, scope);
+
+			Demo.UniformBuffers uniformBuffers = Demo.createUniformBuffers(device, descriptorSetLayout, UniformBufferObject.LAYOUT, frameCount, alloc, scope);
 
 			// could move creation to Chain?
 			VkCommandBufferBeginInfo begin = VkCommandBufferBeginInfo.create(0, null, frame);
@@ -185,6 +199,7 @@ public class Tutorial {
 			cleanup.add(() -> {
 				indexBuffer.close(device.device());
 				vertexBuffer.close(device.device());
+				device.device().vkDestroyDescriptorSetLayout(descriptorSetLayout);
 			});
 
 			for (int i = 0; i < commandBuffers.size(); i++) {
@@ -200,21 +215,60 @@ public class Tutorial {
 
 				cmd.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
 				cmd.vkCmdBindVertexBuffers(0, 1, buffers, buffersOffset);
+				cmd.vkCmdBindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, 1, uniformBuffers.descriptorSets().asSlice(i, 1), 0, null);
 				cmd.vkCmdBindIndexBuffer(indexBuffer.buffer(), indexBuffer.offset(), Vulkan.VK_INDEX_TYPE_UINT16);
 				cmd.vkCmdDrawIndexed(indices.length, 1, 0, 0, 0);
 
 				cmd.vkCmdEndRenderPass();
 				cmd.vkEndCommandBuffer();
 			}
-			return new RenderInfo(device, chain, pipeline, layout, commandBuffers, frameBuffers, cleanup.toArray(Runnable[]::new), scope);
+			return new RenderInfo(device, chain, pipeline, layout, commandBuffers, frameBuffers, uniformBuffers, cleanup.toArray(Runnable[]::new), scope);
 		} catch (Throwable t) {
 			scope.close();
 			throw new RuntimeException(t);
 		}
 	}
 
+	static VkDescriptorSetLayout createDescriptorSetLayout(Demo.DemoDevice device, MemorySession scope) {
+		try ( Frame frame = Frame.frame()) {
+			VkDescriptorSetLayoutBinding uboLayoutBinding = VkDescriptorSetLayoutBinding.create(0, Vulkan.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, Vulkan.VK_SHADER_STAGE_VERTEX_BIT, null, frame);
+			VkDescriptorSetLayoutCreateInfo layoutInfo = VkDescriptorSetLayoutCreateInfo.create(0, 1, uboLayoutBinding, frame);
+
+			return device.device().vkCreateDescriptorSetLayout(layoutInfo, scope);
+		}
+	}
+
+	static long start = System.currentTimeMillis();
+
+	static void updateUniformBuffer(RenderInfo info, int currentImage) {
+		float time = (System.currentTimeMillis() - start) * 1E-3f;
+
+		float[] model;
+		float[] view = new float[16];
+		float[] proj = new float[16];
+
+		model = quat.fromAngle(time * (float)Math.toRadians(90), 0, 0, 1).toMatrix().v;
+		GLMaths.lookAt(view, new float[]{2, 2, 2}, new float[]{0, 0, 0}, new float[]{0, 0, 1});
+		GLMaths.perspective(proj, (float)Math.toRadians(45), (float)info.chain.extWidth() / info.chain.extHeight(), 0.1f, 10f);
+		//proj[5] *= -1;
+
+		try ( Frame frame = Frame.frame()) {
+			UniformBufferObject ubo = UniformBufferObject.create(frame);
+
+			ubo.model().copyFrom(MemorySegment.ofArray(model));
+			ubo.view().copyFrom(MemorySegment.ofArray(view));
+			ubo.proj().copyFrom(MemorySegment.ofArray(proj));
+
+			var memory = info.uniformBuffers().uniformBuffers()[currentImage].memory();
+			MemorySegment buf = info.device.device().vkMapMemory(memory, 0, UniformBufferObject.LAYOUT.byteSize(), 0, frame.scope());
+
+			buf.copyFrom(ubo.segment);
+			info.device.device().vkUnmapMemory(memory);
+		}
+	}
+
 	public static void main(String[] args) throws IOException, InterruptedException {
-		ResourceScope scope = ResourceScope.globalScope();
+		MemorySession scope = MemorySession.global();
 
 		System.loadLibrary("vulkan");
 		System.loadLibrary("X11");
@@ -281,8 +335,7 @@ public class Tutorial {
 outer:      while (true) {
 				int res;
 
-				System.out.println(currentFrame);
-				for (Event e = window.nextEvent(true); e != null; e = window.nextEvent(false)) {
+				for (Event e = window.nextEvent(false); e != null; e = window.nextEvent(false)) {
 					System.out.println(e);
 					switch (e.type) {
 					case Event.KEY:
@@ -309,9 +362,9 @@ outer:      while (true) {
 					}
 				}
 
-				long next = System.nanoTime();
+				//long next = System.nanoTime();
 				//System.out.printf(" %12.9f\n", (next - last) * 1E-9);
-				last = next;
+				//last = next;
 
 				res = device.device().vkAcquireNextImageKHR(chain.swapchain(), ~0, imageReady.get(currentFrame), null, imageIndex);
 				if (res == VK_SUCCESS || res == VK_SUBOPTIMAL_KHR) {
@@ -332,6 +385,8 @@ outer:      while (true) {
 						device.device().vkResetFences(1, renderCurrent);
 					}
 
+					updateUniformBuffer(render, index);
+
 					submitDraw.setWaitSemaphores(imageReady.asSlice(currentFrame));
 					submitDraw.setSignalSemaphores(renderDone.asSlice(currentFrame));
 					submitDraw.setCommandBuffers(commandBuffers.asSlice(index));
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/mat.java b/src/notzed.vulkan.test/classes/vulkan/test/mat.java
new file mode 100644
index 0000000..049a9d1
--- /dev/null
+++ b/src/notzed.vulkan.test/classes/vulkan/test/mat.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package vulkan.test;
+
+import java.io.PrintStream;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+
+public class mat {
+
+	public final float[] v;
+	public final int m;
+	public final int n;
+
+	public mat(int m, int n) {
+		this.m = m;
+		this.n = n;
+		this.v = new float[m * n];
+	}
+
+	public mat(int m, int n, float... v) {
+		this.m = m;
+		this.n = n;
+		this.v = v;
+	}
+
+	public float get(int i, int j) {
+		return v[i * n + j];
+	}
+
+	public void set(int i, int j, float val) {
+		v[i * n + j] = val;
+	}
+
+	public void swapRow(int a, int b) {
+		for (int j = 0; j < n; j++) {
+			float c = v[a * n + j];
+			float d = v[b * n + j];
+			v[a * n + j] = d;
+			v[b * n + j] = c;
+		}
+	}
+
+	public static mat mul(mat A, mat B) {
+		mat C = new mat(A.m, B.n);
+		float[] a = A.v;
+		float[] b = B.v;
+		float[] c = C.v;
+
+		for (int i = 0; i < C.m; i++) {
+			for (int j = 0; j < C.n; j++) {
+				float v = 0;
+				//System.out.printf("[%d]\n", i * C.n + j);
+				for (int k = 0; k < B.m; k++) {
+					//System.out.printf(" %8.3f * %8.3f\n",
+					//	a[i * A.n + k], b[j + B.n * k]);
+
+					v += a[i * A.n + k] * b[j + B.n * k];
+				}
+				//System.out.printf("  = %f\n", v);
+
+				c[i * C.n + j] = v;
+			}
+		}
+
+		return C;
+	}
+
+	public static mat transpose(mat A) {
+		float a[] = A.v;
+		float t[] = new float[a.length];
+		int n = A.n;
+		int m = A.m;
+
+		for (int j = 0; j < n; j++) {
+			for (int i = 0; i < m; i++) {
+				t[j * m + i] = a[i * n + j];
+			}
+		}
+
+		return new mat(A.n, A.m, t);
+	}
+
+//    Copyright (C) 2009 Ed Rosten (er258@cam.ac.uk)
+//All rights reserved.
+//
+//Redistribution and use in source and binary forms, with or without
+//modification, are permitted provided that the following conditions
+//are met:
+//1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+//2. Redistributions in binary form must reproduce the above copyright
+//   notice, this list of conditions and the following disclaimer in the
+//   documentation and/or other materials provided with the distribution.
+//
+//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
+//AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+//ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
+//LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+//CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+//SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+//INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+//CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+//ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+//POSSIBILITY OF SUCH DAMAGE.
+	public static mat invert(mat A) {
+		// Augmentation is in separate matrix, this is a bit faster
+		mat m = new mat(A.m, A.n, A.v.clone());
+		mat n = new mat(A.m, A.m);
+
+		if (m.v.length < m.m * m.n || n.v.length < n.m * n.n)
+			throw new ArrayIndexOutOfBoundsException();
+
+		for (int i = 0; i < n.m; i++) {
+			n.v[i * n.n + i] = 1;
+		}
+
+		//Loop over columns to reduce.
+		for (int col = 0; col < m.m; col++) {
+			//Reduce the current column to a single element
+			{
+				int piv = col;
+				float max = Math.abs(m.v[piv * m.n + col]);
+				for (int p = col + 1; p < m.m; p++)
+					if (Math.abs(m.v[p * m.n + col]) > max) {
+						piv = p;
+						max = Math.abs(m.v[piv * m.n + col]);
+					}
+
+				if (col != piv) {
+					m.swapRow(col, piv);
+					n.swapRow(col, piv);
+				}
+			}
+
+			//Reduce the current column in every row to zero, excluding elements on
+			//the leading diagonal.
+			for (int row = 0; row < m.m; row++) {
+				if (row != col) {
+					float multiple = m.v[row * m.n + col] / m.v[col * m.n + col];
+
+					//Subtract the pivot row from all other rows, to make
+					//column col zero.
+					//m.v[row * m.n + col] = 0;
+					for (int c = col + 1; c < m.n; c++)
+						m.v[row * m.n + c] = m.v[row * m.n + c] - m.v[col * m.n + c] * multiple;
+					for (int c = 0; c < n.n; c++)
+						n.v[row * n.n + c] = n.v[row * n.n + c] - n.v[col * n.n + c] * multiple;
+				}
+			}
+		}
+
+		//Final pass to make diagonal elements one. Performing this in a final
+		//pass allows us to avoid any significant computations on the left-hand
+		//square matrix, since it is diagonal, and ends up as the identity.
+		for (int row = 0; row < m.m; row++) {
+			float mul = 1 / m.v[row * m.n + row];
+
+			for (int col = 0; col < n.n; col++) {
+				n.v[row * n.n + col] *= mul;
+			}
+		}
+
+		return n;
+	}
+
+	public void print(String name) {
+		print(name, System.out);
+	}
+
+	public void print(String name, PrintStream out) {
+		NumberFormat x = new DecimalFormat("#0.000");
+
+		out.print(name);
+		out.print(" = [");
+		out.print(m);
+		out.print(",");
+		out.print(n);
+		out.println("]");
+
+		for (int i = 0; i < m; i++) {
+			for (int j = 0; j < n; j++) {
+				String y = x.format(v[i * n + j]);
+				out.print(" ");
+				out.print("        ".substring(y.length()));
+				out.print(y);
+			}
+			out.println();
+		}
+	}
+
+}
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/mat4.java b/src/notzed.vulkan.test/classes/vulkan/test/mat4.java
new file mode 100644
index 0000000..cccf4ab
--- /dev/null
+++ b/src/notzed.vulkan.test/classes/vulkan/test/mat4.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation +either version 3 of the License +or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not +see <http://www.gnu.org/licenses/>.
+ */
+package vulkan.test;
+
+import au.notzed.nativez.Memory;
+import java.lang.foreign.MemoryLayout;
+
+/**
+ * Partial mat4 implementation.
+ */
+public class mat4 extends mat {
+
+	public mat4() {
+		super(4, 4);
+	}
+
+	public mat4(float... v) {
+		super(4, 4, v);
+
+		if (v.length != 16)
+			throw new IllegalArgumentException();
+	}
+
+	public static final MemoryLayout LAYOUT = MemoryLayout.sequenceLayout(4 * 4, Memory.FLOAT).withBitAlignment(16 * 8);
+
+	final static float I[] = {
+		1, 0, 0, 0,
+		0, 1, 0, 0,
+		0, 0, 1, 0,
+		0, 0, 0, 1
+	};
+
+	public static mat4 I() {
+		return new mat4(I.clone());
+	}
+
+	public static mat4 translate(float dx, float dy, float dz) {
+		return new mat4(
+			1, 0, 0, dx,
+			0, 1, 0, dy,
+			0, 0, 1, dz,
+			0, 0, 0, 1);
+	}
+
+	public static mat4 postTranslate(mat4 M, float dx, float dy, float dz) {
+		float[] a = M.v;
+		float a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
+		float a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
+		float a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
+		float a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
+		// 1 0 0 dx     a b c d
+		// 0 1 0 dy  x  e f g h
+		// 0 0 1 dz     i j k l
+		// 0 0 0 1      m n o p
+		return new mat4(
+			a00 + a30 * dx, a01 + a31 * dx, a02 + a32 * dx, a03 + a33 * dx,
+			a10 + a30 * dy, a11 + a31 * dy, a12 + a32 * dy, a13 + a33 * dy,
+			a20 + a30 * dz, a21 + a31 * dz, a22 + a32 * dz, a23 + a33 * dz,
+			a30, a31, a32, a33);
+	}
+
+	public static mat4 preTranslate(mat4 M, float dx, float dy, float dz) {
+		float[] a = M.v;
+		float a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
+		float a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
+		float a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
+		float a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
+		// a b c d     1 0 0 dx
+		// e f g h  x  0 1 0 dy
+		// i j k l     0 0 1 dz
+		// m n o p     0 0 0 1
+		return new mat4(
+			a00, a01, a02, a00 * dx + a01 * dy + a02 * dz + a03,
+			a10, a11, a12, a10 * dx + a11 * dy + a12 * dz + a13,
+			a20, a21, a22, a20 * dx + a21 * dy + a22 * dz + a23,
+			a30, a31, a32, a30 * dx + a31 * dy + a32 * dz + a33);
+	}
+
+	/*
+	 array-of-struct (aos) version.  i.e. packed vec4 array.
+
+	 This is inefficient for SIMD.
+
+	 a b c d   xyzw xyzw ...
+	 e f g h
+	 i j k l
+	 m n o p
+
+	 x' = a*x+b*y+c*z+d*w
+	 y' = e*x+f*y+g*z+h*w
+	 z' = i*x+j*y+k*z+l*w
+	 w' = m*x+n*y+o*z+p*w
+	 */
+	public static void apply(mat4 A, int n, float[] vec, float[] res) {
+		float a00 = A.v[0];
+		float a01 = A.v[1];
+		float a02 = A.v[2];
+		float a03 = A.v[3];
+		float a10 = A.v[4];
+		float a11 = A.v[5];
+		float a12 = A.v[6];
+		float a13 = A.v[7];
+		float a20 = A.v[8];
+		float a21 = A.v[9];
+		float a22 = A.v[10];
+		float a23 = A.v[11];
+		float a30 = A.v[12];
+		float a31 = A.v[13];
+		float a32 = A.v[14];
+		float a33 = A.v[15];
+
+		for (int i = 0; i < n * 4; i += 4) {
+			float v0 = vec[i + 0];
+			float v1 = vec[i + 1];
+			float v2 = vec[i + 2];
+			float v3 = vec[i + 3];
+			res[i + 0] = a00 * v0 + a01 * v1 + a02 * v2 + a03 * v3;
+			res[i + 1] = a10 * v0 + a11 * v1 + a12 * v2 + a13 * v3;
+			res[i + 2] = a20 * v0 + a21 * v1 + a22 * v2 + a23 * v3;
+			res[i + 3] = a30 * v0 + a31 * v1 + a32 * v2 + a33 * v3;
+		}
+	}
+
+
+	/*
+
+	struct-of-array (soa) version
+
+	 a b c d   xxxx ...
+	 e f g h   yyyy ...
+	 i j k l   zzzz ...
+	 m n o p   wwww ...
+
+	 x' = a*x+b*y+c*z+d*w
+	 y' = e*x+f*y+g*z+h*w
+	 z' = i*x+j*y+k*z+l*w
+	 w' = m*x+n*y+o*z+p*w
+
+	 */
+	public static void apply4(mat4 A, int n, float[] vec, int stride, float[] res) {
+		float a00 = A.v[0];
+		float a01 = A.v[1];
+		float a02 = A.v[2];
+		float a03 = A.v[3];
+		float a10 = A.v[4];
+		float a11 = A.v[5];
+		float a12 = A.v[6];
+		float a13 = A.v[7];
+		float a20 = A.v[8];
+		float a21 = A.v[9];
+		float a22 = A.v[10];
+		float a23 = A.v[11];
+		float a30 = A.v[12];
+		float a31 = A.v[13];
+		float a32 = A.v[14];
+		float a33 = A.v[15];
+
+		for (int i = 0; i < n; i += 1) {
+			float v0 = vec[i + stride * 0];
+			float v1 = vec[i + stride * 1];
+			float v2 = vec[i + stride * 2];
+			float v3 = vec[i + stride * 3];
+
+			res[i + stride * 0] = a00 * v0 + a01 * v1 + a02 * v2 + a03 * v3;
+			res[i + stride * 1] = a10 * v0 + a11 * v1 + a12 * v2 + a13 * v3;
+			res[i + stride * 2] = a20 * v0 + a21 * v1 + a22 * v2 + a23 * v3;
+			res[i + stride * 3] = a30 * v0 + a31 * v1 + a32 * v2 + a33 * v3;
+		}
+	}
+
+	/*
+	 a b c d   x
+	 e f g h   y
+	 i j k l   z
+	 m n o p   w
+
+	 x' = a*x+b*y+c*z+d*w
+	 y' = e*x+f*y+g*z+h*w
+	 z' = i*x+j*y+k*z+l*w
+	 w' = m*x+n*y+o*z+p*w
+
+	 */
+	public static mat4 mul(mat4 A, mat4 B) {
+		float[] a = A.v;
+		float[] b = B.v;
+		float[] c = new float[16];
+
+		for (int i = 0; i < 4; i++) {
+			for (int j = 0; j < 4; j++) {
+				c[i * 4 + j] = a[i * 4] * b[j] + a[i * 4 + 1] * b[j + 4] + a[i * 4 + 2] * b[j + 8] + a[i * 4 + 3] * b[j + 12];
+			}
+		}
+
+		return new mat4(c);
+	}
+
+	// calculate C' = A x B'
+	//  - B is in col-major order
+	//  - C is in col-major order
+	public static mat mul_NT_T(mat4 A, mat B) {
+		float[] a = A.v;
+		float[] b = B.v;
+		float[] c = new float[b.length];
+		float a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
+		float a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
+		float a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
+		float a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
+
+		for (int x = 0; x < c.length; x += 4) {
+			float b0 = b[x + 0];
+			float b1 = b[x + 1];
+			float b2 = b[x + 2];
+			float b3 = b[x + 3];
+
+			c[x + 0] = a00 * b0 + a01 * b1 + a02 * b2 + a03 * b3;
+			c[x + 1] = a10 * b0 + a11 * b1 + a12 * b2 + a13 * b3;
+			c[x + 2] = a20 * b0 + a21 * b1 + a22 * b2 + a23 * b3;
+			c[x + 3] = a30 * b0 + a31 * b1 + a32 * b2 + a33 * b3;
+		}
+
+		return new mat(B.m, B.n, c);
+	}
+
+	public static mat4 invert(mat4 A) {
+		// Augmentation is in separate matrix, this is a bit faster
+		// Loop bounds are constant, this is quite a bit faster
+		mat4 m = new mat4(A.v.clone());
+		//mat4 n = new mat4();
+
+		//for (int i = 0; i < 4; i++) {
+		//	n.v[i * 4 + i] = 1;
+		//}
+		mat4 n = new mat4(I.clone());
+
+		if (m.v.length < 16 || n.v.length < 16)
+			throw new ArrayIndexOutOfBoundsException();
+
+		//Loop over columns to reduce.
+		for (int col = 0; col < 4; col++) {
+			//Reduce the current column to a single element
+			{
+				int piv = col;
+				float max = Math.abs(m.v[piv * 4 + col]);
+				for (int p = col + 1; p < 4; p++)
+					if (Math.abs(m.v[p * 4 + col]) > max) {
+						piv = p;
+						max = Math.abs(m.v[piv * 4 + col]);
+					}
+
+				if (col != piv) {
+					m.swapRow(col, piv);
+					n.swapRow(col, piv);
+				}
+			}
+
+			//Reduce the current column in every row to zero, excluding elements on
+			//the leading diagonal.
+			for (int row = 0; row < 4; row++) {
+				if (row != col) {
+					float multiple = m.v[row * 4 + col] / m.v[col * 4 + col];
+
+					//Subtract the pivot row from all other rows, to make
+					//column col zero.
+					//m.v[row * 4 + col] = 0;
+					for (int c = col + 1; c < 4; c++)
+						m.v[row * 4 + c] = m.v[row * 4 + c] - m.v[col * 4 + c] * multiple;
+					for (int c = 0; c < 4; c++)
+						n.v[row * 4 + c] = n.v[row * 4 + c] - n.v[col * 4 + c] * multiple;
+				}
+			}
+		}
+
+		//Final pass to make diagonal elements one. Performing this in a final
+		//pass allows us to avoid any significant computations on the left-hand
+		//square matrix, since it is diagonal, and ends up as the identity.
+		for (int row = 0; row < 4; row++) {
+			float mul = 1 / m.v[row * 4 + row];
+
+			for (int col = 0; col < 4; col++) {
+				n.v[row * 4 + col] *= mul;
+			}
+		}
+
+		return n;
+	}
+
+	// from some gl library
+	public static mat4 inverse(mat4 m) {
+		float[] v = m.v;
+		float a00 = v[0], a01 = v[1], a02 = v[2], a03 = v[3],
+			a10 = v[4], a11 = v[5], a12 = v[6], a13 = v[7],
+			a20 = v[8], a21 = v[9], a22 = v[10], a23 = v[11],
+			a30 = v[12], a31 = v[13], a32 = v[14], a33 = v[15],
+			b00 = a00 * a11 - a01 * a10,
+			b01 = a00 * a12 - a02 * a10,
+			b02 = a00 * a13 - a03 * a10,
+			b03 = a01 * a12 - a02 * a11,
+			b04 = a01 * a13 - a03 * a11,
+			b05 = a02 * a13 - a03 * a12,
+			b06 = a20 * a31 - a21 * a30,
+			b07 = a20 * a32 - a22 * a30,
+			b08 = a20 * a33 - a23 * a30,
+			b09 = a21 * a32 - a22 * a31,
+			b10 = a21 * a33 - a23 * a31,
+			b11 = a22 * a33 - a23 * a32,
+			det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
+
+		return new mat4(
+			(a11 * b11 - a12 * b10 + a13 * b09) / det,
+			(a02 * b10 - a01 * b11 - a03 * b09) / det,
+			(a31 * b05 - a32 * b04 + a33 * b03) / det,
+			(a22 * b04 - a21 * b05 - a23 * b03) / det,
+			(a12 * b08 - a10 * b11 - a13 * b07) / det,
+			(a00 * b11 - a02 * b08 + a03 * b07) / det,
+			(a32 * b02 - a30 * b05 - a33 * b01) / det,
+			(a20 * b05 - a22 * b02 + a23 * b01) / det,
+			(a10 * b10 - a11 * b08 + a13 * b06) / det,
+			(a01 * b08 - a00 * b10 - a03 * b06) / det,
+			(a30 * b04 - a31 * b02 + a33 * b00) / det,
+			(a21 * b02 - a20 * b04 - a23 * b00) / det,
+			(a11 * b07 - a10 * b09 - a12 * b06) / det,
+			(a00 * b09 - a01 * b07 + a02 * b06) / det,
+			(a31 * b01 - a30 * b03 - a32 * b00) / det,
+			(a20 * b03 - a21 * b01 + a22 * b00) / det);
+	}
+
+}
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/quat.java b/src/notzed.vulkan.test/classes/vulkan/test/quat.java
new file mode 100644
index 0000000..eba150f
--- /dev/null
+++ b/src/notzed.vulkan.test/classes/vulkan/test/quat.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package vulkan.test;
+
+import static java.lang.Math.cos;
+import static java.lang.Math.sin;
+import static java.lang.Math.sqrt;
+
+public class quat {
+
+	private final float x, y, z, w;
+
+	quat(float x, float y, float z, float w) {
+		this.x = x;
+		this.y = y;
+		this.z = z;
+		this.w = w;
+	}
+
+	public String toString() {
+		return String.format("[%8.3f %8.3fi %8.3fj %8.3fk]",
+			w, x, y, z);
+	}
+
+	static quat mul(quat p, quat q) {
+		float a = p.w;
+		float b = p.x;
+		float c = p.y;
+		float d = p.z;
+
+		float e = q.w;
+		float f = q.x;
+		float g = q.y;
+		float h = q.z;
+
+		return new quat(
+			b * e + a * f + c * h - d * g,
+			a * g - b * h + c * e + d * f,
+			a * h + b * g - c * f + d * e,
+			a * e - b * f - c * g - d * h);
+	}
+
+	static quat fromVector(float x, float y, float z) {
+		float s = 1.0f / (float)sqrt(x*x+y*y+z*z);
+
+		return new quat(x*s, y*s, z*s, 0);
+	}
+
+	static quat fromAngle(float a, float x, float y, float z) {
+		float ca = (float)cos(a * 0.5f);
+		float sa = (float)sin(a * 0.5f)
+			/ (float)sqrt(x * x + y * y + z * z);
+
+		return new quat(x * sa, y * sa, z * sa, ca);
+	}
+
+	public mat4 toMatrix() {
+		return new mat4(
+			1 - 2 * z * z - 2 * y * y, 2 * x * y + 2 * z * w, 2 * x * z - 2 * y * w, 0,
+			2 * x * y - 2 * z * w, 1 - 2 * x * x - 2 * z * z, 2 * y * z + 2 * x * w, 0,
+			2 * x * z + 2 * y * w, 2 * y * z - 2 * x * w, 1 - 2 * x * x - 2 * y * y, 0,
+			0, 0, 0, 1
+		);
+	}
+}
diff --git a/src/notzed.vulkan.test/gen/generate-shaderio b/src/notzed.vulkan.test/gen/generate-shaderio
index c2c2485..adf50c8 100755
--- a/src/notzed.vulkan.test/gen/generate-shaderio
+++ b/src/notzed.vulkan.test/gen/generate-shaderio
@@ -19,12 +19,12 @@ import au.notzed.nativez.Memory;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.channels.Channels;
-import jdk.incubator.foreign.MemorySegment;
-import jdk.incubator.foreign.SegmentAllocator;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.SegmentAllocator;
 
 public class ShaderIO {
 	static IntArray loadSPIRV(String name, long size, SegmentAllocator alloc) throws IOException {
-		try ( InputStream is = TestMandelbrot.class.getResourceAsStream(name)) {
+		try (InputStream is = TestMandelbrot.class.getResourceAsStream(name)) {
 			MemorySegment seg = alloc.allocateArray(Memory.INT, size);
 			int length = Channels.newChannel(is).read(seg.asByteBuffer());
 			return IntArray.create(seg.asSlice(0, length));
diff --git a/src/notzed.vulkan.test/shaders/vulkan/test/tutorial.vert b/src/notzed.vulkan.test/shaders/vulkan/test/tutorial.vert
index 48bd441..5ffbb2d 100644
--- a/src/notzed.vulkan.test/shaders/vulkan/test/tutorial.vert
+++ b/src/notzed.vulkan.test/shaders/vulkan/test/tutorial.vert
@@ -1,11 +1,17 @@
 #version 450
 
-layout (location=0) in vec2 inPosition;
-layout (location=1) in vec3 inColour;
+layout(binding = 0) uniform UniformBufferObject {
+    mat4 model;
+    mat4 view;
+    mat4 proj;
+} ubo;
 
-layout (location=0) out vec3 colour;
+layout(location = 0) in vec2 inPosition;
+layout(location = 1) in vec3 inColor;
+
+layout(location = 0) out vec3 fragColor;
 
 void main() {
-	gl_Position = vec4(inPosition, 0.0, 1.0);
-	colour = inColour;
+    gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
+    fragColor = inColor;
 }
diff --git a/src/notzed.vulkan/gen/command-types.api b/src/notzed.vulkan/gen/command-types.api
index e8f02f1..e85548e 100644
--- a/src/notzed.vulkan/gen/command-types.api
+++ b/src/notzed.vulkan/gen/command-types.api
@@ -62,7 +62,7 @@ code method-query {
 
 code method-extension {
   dispatch {{
-	final NativeSymbol {name}$NS;
+	final MemorySegment {name}$NS;
   }}
   invoke {{
 	/* method-extension:invoke */
@@ -77,7 +77,7 @@ code method-extension {
 		{native-result-define}
 		try {create-frame}{
 			{native-init}
-			{native-result-assign}{name}$DH.invokeExact(dispatch.{name}$NS, {invoke-arg});
+			{native-result-assign}{name}$DH.invokeExact((Addressable)dispatch.{name}$NS, {invoke-arg});
 			{result-test}{
 				{java-result-assign}
 				{java-result-return}
@@ -92,7 +92,7 @@ code method-extension {
 
 code method-extension-query {
   dispatch {{
-	final NativeSymbol {name}$NS;
+	final MemorySegment {name}$NS;
   }}
   invoke {{
 	/* method-extension:invoke */
@@ -107,10 +107,10 @@ code method-extension-query {
 		{native-result-define}
 		try {create-frame}{
 			{native-init}
-			{native-result-assign}{name}$DH.invokeExact(dispatch.{name}$NS, {query-arg});
+			{native-result-assign}{name}$DH.invokeExact((Addressable)dispatch.{name}$NS, {query-arg});
 			{result-test}{
 				{query-init}
-				{native-result-assign}{name}$DH.invokeExact(dispatch.{name}$NS, {invoke-arg});
+				{native-result-assign}{name}$DH.invokeExact((Addressable)dispatch.{name}$NS, {invoke-arg});
 				{java-result-assign}
 				{java-result-return}
 			}
@@ -133,7 +133,7 @@ code funcpointer {
 		{function-descriptor};
   }}
   upcall {{
-	public static FunctionPointer<{rename}> upcall({rename} target$, ResourceScope scope$) {
+	public static FunctionPointer<{rename}> upcall({rename} target$, MemorySession scope$) {
 		interface Trampoline {
 			{native-result} call({native-arg});
 		}
@@ -166,7 +166,7 @@ code funcpointer-readwrite insert=funcpointer:common,funcpointer:upcall,funcpoin
   class {{
 	package {package};
 
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 
@@ -304,7 +304,7 @@ type pointer value {
 	carrier	{{ MemoryAddress }}
 	layout	{{ Memory.POINTER }}
 	type	{{ MemoryAddress }}
-	sig		{{ Ljdk/incubator/foreign/MemoryAddress; }}
+	sig		{{ Ljava/lang/foreign/MemoryAddress; }}
 
 	invoke-arg	{{ Memory.address({name}) }}
 }
@@ -318,7 +318,7 @@ type void {
 type void* pointer;
 
 type funcpointer pointer {
-	type		{{ NativeSymbol }}
+	type		{{ MemorySegment }}
 }
 
 # FIXME: clenaup, value-pointer does nothing
@@ -509,7 +509,7 @@ type void*-return void* {
 type funcpointer-return funcpointer need-scope {
 	native-result-define	{{ MemoryAddress result$; }}
 	native-result-assign	{{ result$ = (MemoryAddress) }}
-	java-result-return		{{ return NativeSymbol.ofAddress(pName, result$, scope$); }}
+	java-result-return		{{ return MemorySegment.ofAddress(result$, 0, scope$); }}
 }
 
 type uint32_t-return uint32_t {
@@ -668,7 +668,7 @@ override commands {
 	PFN_vkDebugReportCallbackEXT pUserData=type:void*-ignore;
 	PFN_vkDeviceMemoryReportCallbackEXT pUserData=type:void*-ignore;
 
-	# Mapped to a NativeSymbol via 'funcpointer' type
+	# Mapped to a MemorySegment via 'funcpointer' type
 	PFN_vkVoidFunction ignore;
 
 	# Don't need VkAllocationCallbacks, don't need these
diff --git a/src/notzed.vulkan/gen/generate-vulkan b/src/notzed.vulkan/gen/generate-vulkan
index 89d8876..dcb76c6 100755
--- a/src/notzed.vulkan/gen/generate-vulkan
+++ b/src/notzed.vulkan/gen/generate-vulkan
@@ -1363,10 +1363,10 @@ sub collectFunctionInfo {
 	}
 
 	$info->{'create-frame'} = '(Frame frame$ = Frame.frame())' if $needFrame;
-	$info->{'trampoline-scope'} = '(ResourceScope scope$$ = ResourceScope.newConfinedScope())' if $trampScope;
+	$info->{'trampoline-scope'} = '(MemorySession scope$$ = MemorySession.openConfined())' if $trampScope;
 
 	push @{$info->{'java-arg'}}, 'SegmentAllocator alloc$' if $needAlloc;
-	push @{$info->{'java-arg'}}, 'ResourceScope scope$' if $needScope;
+	push @{$info->{'java-arg'}}, 'MemorySession scope$' if $needScope;
 
 	foreach my $field (@arrayFields) {
 		my $with = $field =~ m/-arg$/n ? ",\n\t" : "\n\t";
diff --git a/src/notzed.vulkan/gen/struct-types.api b/src/notzed.vulkan/gen/struct-types.api
index 77c61ae..b9a784d 100644
--- a/src/notzed.vulkan/gen/struct-types.api
+++ b/src/notzed.vulkan/gen/struct-types.api
@@ -127,14 +127,14 @@ END
 code handle-array {
   getorset {{
 	/* value-array {deref} */
-	public {type} get{Name}(VkInstance instance$, ResourceScope scope$) {
+	public {type} get{Name}(VkInstance instance$, MemorySession scope$) {
 		try {
 			return {java-get};
 		} catch (Throwable t) {
 			throw new RuntimeException(t);
 		}
 	}
-	public {typei} get{Name}Element(int i$, VkInstance instance$, ResourceScope scope$) {
+	public {typei} get{Name}Element(int i$, VkInstance instance$, MemorySession scope$) {
 		return {java-geti};
 	}
 	public void set{Name}Element(int i$, {typei} {name}) {
@@ -366,7 +366,7 @@ code Vulkan {
 	package {package};
 
 	import au.notzed.nativez.Memory;
-	import jdk.incubator.foreign.MemorySegment;
+	import java.lang.foreign.MemorySegment;
 	import java.util.Arrays;
 
 	public class Vulkan {
@@ -413,19 +413,19 @@ code dispatch {
   class {{
 	// template: dispatch:class
 	package {package};
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 	class {Name} {
 
 		{field-init}
 
-		{Name}(VkInstance instance$, ResourceScope scope$) {
+		{Name}(VkInstance instance$, MemorySession scope$) {
 			{init}
 		}
 	}
   }}
-  field-init {{	final NativeSymbol {name}$NS; }}
+  field-init {{	final MemorySegment {name}$NS; }}
   init {{ {name}$NS = instance$.vkGetInstanceProcAddr("{name}", scope$); }}
 
 }
@@ -435,24 +435,24 @@ code handle {
   class {{
 	// template: handle:class
 	package {package};
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 
 	public class {name} implements Pointer {
-		final NativeSymbol self;
+		final MemorySegment self;
 
-		private {name}(MemoryAddress address, ResourceScope scope) {
-			this.self = NativeSymbol.ofAddress("{name}", address, scope);
+		private {name}(MemoryAddress address, MemorySession scope) {
+			this.self = MemorySegment.ofAddress(address, 0, scope);
 			{init}
 		}
 
-		public static {name} create(MemoryAddress address, ResourceScope scope) {
+		public static {name} create(MemoryAddress address, MemorySession scope) {
 			return address != MemoryAddress.NULL ? new {name}(address, scope) : null;
 		}
 
 		/** Allocate an array where the handle scope is independent of the array allocation */
-		public static HandleArray<{name}> createArray(long length, SegmentAllocator alloc, ResourceScope scope$) {
+		public static HandleArray<{name}> createArray(long length, SegmentAllocator alloc, MemorySession scope$) {
 			return HandleArray.createArray(length, alloc, (a, s) -> create(a, scope$));
 		}
 
@@ -465,8 +465,8 @@ code handle {
 			return self.address();
 		}
 
-		public ResourceScope scope() {
-			return self.scope();
+		public MemorySession scope() {
+			return self.session();
 		}
 
 		public String toString() {
@@ -481,21 +481,21 @@ code handle-instance {
   class {{
 	// template: handle-instance:class
 	package {package};
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 
 	public class {name} implements Pointer {
-		final NativeSymbol self;
+		final MemorySegment self;
 		final DispatchInstance dispatch;
 
-		private {name}(MemoryAddress address, ResourceScope scope) {
-			this.self = NativeSymbol.ofAddress("{name}", address, scope);
+		private {name}(MemoryAddress address, MemorySession scope) {
+			this.self = MemorySegment.ofAddress(address, 0, scope);
 			this.dispatch = new DispatchInstance(this, scope);
 			{init}
 		}
 
-		public static {name} create(MemoryAddress address, ResourceScope scope) {
+		public static {name} create(MemoryAddress address, MemorySession scope) {
 			return new {name}(address, scope);
 		}
 
@@ -503,8 +503,8 @@ code handle-instance {
 			return self.address();
 		}
 
-		public ResourceScope scope() {
-			return self.scope();
+		public MemorySession scope() {
+			return self.session();
 		}
 
 		{commands}
@@ -517,30 +517,30 @@ code handle-dispatch {
   class {{
 	// template: handle-dispatch:class
 	package {package};
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 
 	public class {name} implements Pointer {
-		final NativeSymbol self;
+		final MemorySegment self;
 		final DispatchInstance dispatch;
 
-		private {name}(MemoryAddress address, DispatchInstance dispatch, ResourceScope scope) {
-			this.self = NativeSymbol.ofAddress("{name}", address, scope);
+		private {name}(MemoryAddress address, DispatchInstance dispatch, MemorySession scope) {
+			this.self = MemorySegment.ofAddress(address, 0, scope);
 			this.dispatch = dispatch;
 			{init}
 		}
 
-		public static {name} create(MemoryAddress address, DispatchInstance dispatch, ResourceScope scope) {
+		public static {name} create(MemoryAddress address, DispatchInstance dispatch, MemorySession scope) {
 			return new {name}(address, dispatch, scope);
 		}
 
 		// TODO: evaluate how scope fits here
-		public static HandleArray<{name}> createArray(long length, SegmentAllocator alloc, DispatchInstance dispatch, ResourceScope scope) {
+		public static HandleArray<{name}> createArray(long length, SegmentAllocator alloc, DispatchInstance dispatch, MemorySession scope) {
 			return HandleArray.createArray(length, alloc, (a, s) -> create(a, dispatch, s), scope);
 		}
 
-		public static HandleArray<{name}> createArray(long length, SegmentAllocator alloc, VkInstance instance, ResourceScope scope) {
+		public static HandleArray<{name}> createArray(long length, SegmentAllocator alloc, VkInstance instance, MemorySession scope) {
 			return HandleArray.createArray(length, alloc, (a, s) -> create(a, instance.dispatch, s), scope);
 		}
 
@@ -548,8 +548,8 @@ code handle-dispatch {
 			return self.address();
 		}
 
-		public ResourceScope scope() {
-			return self.scope();
+		public MemorySession scope() {
+			return self.session();
 		}
 
 		{commands}
@@ -576,7 +576,7 @@ code struct {
 			return new {Name}(segment);
 		}
 
-		public static {Name} create(MemoryAddress address, ResourceScope scope) {
+		public static {Name} create(MemoryAddress address, MemorySession scope) {
 			return create(MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope));
 		}
 
@@ -603,8 +603,8 @@ code struct {
 
 		// Pointer
 		@Override
-		public ResourceScope scope() {
-			return segment.scope();
+		public MemorySession scope() {
+			return segment.session();
 		}
 
 		@Override
@@ -636,7 +636,7 @@ code struct {
 		}
   }}
   array {{
-		static {Name} createArray(MemoryAddress addr, long length, ResourceScope scope) {
+		static {Name} createArray(MemoryAddress addr, long length, MemorySession scope) {
 			return create(MemorySegment.ofAddress(addr, length * LAYOUT.byteSize(), scope));
 		}
 
@@ -646,7 +646,7 @@ code struct {
  			return self$;
 		}
 
-		public final static MethodHandle LAYOUT$SH = MemoryLayout.sequenceLayout(LAYOUT).sliceHandle(MemoryLayout.PathElement.sequenceElement());
+		public final static MethodHandle LAYOUT$SH = MemoryLayout.sequenceLayout(-1, LAYOUT).sliceHandle(MemoryLayout.PathElement.sequenceElement());
 
 		// Array
 		@Override
@@ -676,7 +676,7 @@ code struct-writeonly insert=struct:header,struct:create-all
   class {{
 	// template: struct-writeonly:class
 	package {package};
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 
@@ -698,7 +698,7 @@ code struct-writeonly-array insert=struct:header,struct:create-all,struct:array
   class {{
 	// template: struct-writeonly-array:class
 	package {package};
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 
@@ -727,7 +727,7 @@ code struct-readonly insert=struct:header
   class {{
 	// template: struct-readonly:class
 	package {package};
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 
@@ -749,7 +749,7 @@ code struct-readonly-array insert=struct:header,struct:array
   class {{
 	// template: struct-readonly-array:class
 	package {package};
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 
@@ -776,7 +776,7 @@ code struct-readwrite insert=struct:header,struct:create-all
   class {{
 	// template: struct-readwrite:class
 	package {package};
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 
@@ -801,7 +801,7 @@ code struct-readwrite-array insert=struct:header,struct:create-all,struct:array
   class {{
 	// template: struct-readwrite-array:class
 	package {package};
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 
@@ -833,7 +833,7 @@ code struct-readwrite-all insert=struct:header,struct:create-all,struct:set-all
   class {{
 	// template: struct-readwrite-all:class
 	package {package};
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 
@@ -858,7 +858,7 @@ code struct-readwrite-array-all insert=struct:header,struct:create-all,struct:ar
   class {{
 	// template: struct-readwrite-array-all:class
 	package {package};
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 
@@ -887,7 +887,7 @@ code struct-writeonly-array-all insert=struct:header,struct:create-all,struct:ar
   class {{
 	// template: struct-writeonly-array-all:class
 	package {package};
-	import jdk.incubator.foreign.*;
+	import java.lang.foreign.*;
 	import java.lang.invoke.*;
 	import au.notzed.nativez.*;
 
@@ -923,7 +923,7 @@ type value accessor=value {
 
 	native-getat	{{ ({type}){name}$AH.get(this.segment, i$) }}
 	native-setat	{{ {name}$AH.set(this.segment, i$, {native-value}) }}
-	handleat		{{ final static VarHandle {name}$AH = MemoryLayout.sequenceLayout(LAYOUT).varHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("{name}")); }}
+	handleat		{{ final static VarHandle {name}$AH = MemoryLayout.sequenceLayout(-1, LAYOUT).varHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("{name}")); }}
 
 	java-getat		{{ {native-getat} }}
 	java-setat		{{ {native-setat} }}
@@ -944,7 +944,7 @@ type value-array accessor=value-array {
 
 	native-getat	{{ (MemorySegment){name}$AH.invokeExact(this.segment, i$) }}
 	handleat		{{
-		final static MethodHandle {name}$AH = MemoryLayout.sequenceLayout(LAYOUT).sliceHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("{name}"));
+		final static MethodHandle {name}$AH = MemoryLayout.sequenceLayout(-1, LAYOUT).sliceHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("{name}"));
 	}}
 	java-getat		{{ {type}.create({native-getat}) }}
 }
@@ -967,7 +967,7 @@ type inline accessor=inline {
 
 	native-getat {{ (MemorySegment){name}$SA.invokeExact(this.segment, i$) }}
 	java-getat	{{ {type}.create({native-getat}) }}
-	handleat	{{ final static MethodHandle {name}$SA = MemoryLayout.sequenceLayout(LAYOUT).sliceHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("{name}")); }}
+	handleat	{{ final static MethodHandle {name}$SA = MemoryLayout.sequenceLayout(-1, LAYOUT).sliceHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("{name}")); }}
 }
 
 type inline-array accessor=inline {
@@ -977,7 +977,7 @@ type inline-array accessor=inline {
 
 	native-getat {{ (MemorySegment){name}$SA.invokeExact(this.segment, i$) }}
 	java-getat	{{ {type}.create({native-getat}) }}
-	handleat	{{ final static MethodHandle {name}$SA = MemoryLayout.sequenceLayout(LAYOUT).sliceHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("{name}")); }}
+	handleat	{{ final static MethodHandle {name}$SA = MemoryLayout.sequenceLayout(-1, LAYOUT).sliceHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("{name}")); }}
 }
 
 type uint8_t,char value {
@@ -1074,7 +1074,7 @@ type pointer value {
 type void* pointer;
 
 type value-pointer pointer {
-	java-get	{{ {type}.create((MemoryAddress){name}$VH.get(this.segment), segment.scope()) }}
+	java-get	{{ {type}.create((MemoryAddress){name}$VH.get(this.segment), segment.session()) }}
 }
 
 type uint8_t* value-pointer {
@@ -1098,12 +1098,12 @@ type size_t* value-pointer {
 }
 
 type pointer-length pointer {
-	java-get	{{ {type}.createArray({native-get}, {length}, this.segment.scope()) }}
+	java-get	{{ {type}.createArray({native-get}, {length}, this.segment.session()) }}
 }
 
 type void*-length pointer-length {
 	type		MemorySegment;
-	java-get	{{ MemorySegment.ofAddress({native-get}, {length}, this.segment.scope()) }}
+	java-get	{{ MemorySegment.ofAddress({native-get}, {length}, this.segment.session()) }}
 }
 
 type uint8_t*-length pointer-length {
@@ -1150,7 +1150,7 @@ type char**-length pointer-length accessor=value-alloc {
 type funcpointer pointer {
 	type		{{ FunctionPointer<{baseType}> }}
 	typei		{{ {baseType} }}
-	java-get	{{ {baseType}.downcall({native-get}, this.segment.scope()) }}
+	java-get	{{ {baseType}.downcall({native-get}, this.segment.session()) }}
 }
 
 type void** pointer {
@@ -1162,7 +1162,7 @@ type void**-length pointer-length {
 
 type handle pointer {
 	type		{{ {baseType} }}
-	java-get	{{ {type}.create({native-get}, this.segment.scope()) }}
+	java-get	{{ {type}.create({native-get}, this.segment.session()) }}
 }
 
 type handle[] value-array accessor=handle-array {
@@ -1183,7 +1183,7 @@ type handle* pointer {
 type handle*-length pointer-length {
 	type		{{ HandleArray<{baseType}> }}
 	typei		{{ {baseType} }}
-	java-get	{{ HandleArray.createArray({native-get}, {length}, {typei}::create, this.segment.scope()) }}
+	java-get	{{ HandleArray.createArray({native-get}, {length}, {typei}::create, this.segment.session()) }}
 	java-set	{{ {name}$VH.set(this.segment, Memory.address({name})); }}
 }
 
@@ -1199,7 +1199,7 @@ type struct-expand inline accessor=inline-expand {
 
 type struct* pointer {
 	type		{{ {baseType} }}
-	java-get	{{ {baseType}.create({native-get}, this.segment.scope()) }}
+	java-get	{{ {baseType}.create({native-get}, this.segment.session()) }}
 }
 
 type struct*-length pointer-length {
@@ -1232,7 +1232,7 @@ override structs {
 			VkPipelineCache pipelineCache,
 			VkGraphicsPipelineCreateInfo pCreateInfos,
 			SegmentAllocator alloc$,
-			ResourceScope scope$) {
+			MemorySession scope$) {
 			HandleArray<VkPipeline> pPipelines = VkPipeline.createArray(pCreateInfos.length(), alloc$, scope$);
 			vkCreateGraphicsPipelines(pipelineCache, (int)pCreateInfos.length(), pCreateInfos, pPipelines);
 			// Vulkan.VK_PIPELINE_COMPILE_REQUIRED_EXT ??
@@ -1242,7 +1242,7 @@ override structs {
 		public VkPipeline vkCreateGraphicsPipeline(
 			VkPipelineCache pipelineCache,
 			VkGraphicsPipelineCreateInfo pCreateInfo,
-			ResourceScope scope) {
+			MemorySession scope) {
 			try (Frame alloc$ = Frame.frame()) {
 				HandleArray<VkPipeline> pPipelines = VkPipeline.createArray(1, alloc$, scope);
 				vkCreateGraphicsPipelines(pipelineCache, 1, pCreateInfo, pPipelines);
@@ -1254,7 +1254,7 @@ override structs {
 		public HandleArray<VkCommandBuffer> vkAllocateCommandBuffers(
 				VkCommandBufferAllocateInfo pAllocateInfo,
 				SegmentAllocator alloc$,
-				ResourceScope scope$) {
+				MemorySession scope$) {
 
 				var buffers = VkCommandBuffer.createArray(
 					(int)VkCommandBufferAllocateInfo.commandBufferCount$VH.get(pAllocateInfo.segment),
@@ -1268,7 +1268,7 @@ override structs {
 		public HandleArray<VkDescriptorSet> vkAllocateDescriptorSets(
 				VkDescriptorSetAllocateInfo pAllocateInfo,
 				SegmentAllocator alloc$,
-				ResourceScope scope) {
+				MemorySession scope) {
 
 				var buffers = VkDescriptorSet.createArray(
 					(int)VkDescriptorSetAllocateInfo.descriptorSetCount$VH.get(pAllocateInfo.segment),
@@ -1331,13 +1331,13 @@ override structs {
 			return self;
 		}
 
-		final static VarHandle colour$float32$EH = MemoryLayout.sequenceLayout(LAYOUT).varHandle(
+		final static VarHandle colour$float32$EH = MemoryLayout.sequenceLayout(-1, LAYOUT).varHandle(
 				MemoryLayout.PathElement.sequenceElement(),
 				MemoryLayout.PathElement.groupElement("color"),
 				MemoryLayout.PathElement.groupElement("float32"),
 				MemoryLayout.PathElement.sequenceElement());
 
-		final static VarHandle colour$int32$EH = MemoryLayout.sequenceLayout(LAYOUT).varHandle(
+		final static VarHandle colour$int32$EH = MemoryLayout.sequenceLayout(-1, LAYOUT).varHandle(
 				MemoryLayout.PathElement.sequenceElement(),
 				MemoryLayout.PathElement.groupElement("color"),
 				MemoryLayout.PathElement.groupElement("int32"),
@@ -1356,12 +1356,12 @@ override structs {
 			colour$int32$EH.set(segment, index, 3, a);
 		}
 
-		final static VarHandle depthStencil$depth$EH = MemoryLayout.sequenceLayout(LAYOUT).varHandle(
+		final static VarHandle depthStencil$depth$EH = MemoryLayout.sequenceLayout(-1, LAYOUT).varHandle(
 				MemoryLayout.PathElement.sequenceElement(),
 				MemoryLayout.PathElement.groupElement("depthStencil"),
 				MemoryLayout.PathElement.groupElement("depth"));
 
-		final static VarHandle depthStencil$stencil$EH = MemoryLayout.sequenceLayout(LAYOUT).varHandle(
+		final static VarHandle depthStencil$stencil$EH = MemoryLayout.sequenceLayout(-1, LAYOUT).varHandle(
 				MemoryLayout.PathElement.sequenceElement(),
 				MemoryLayout.PathElement.groupElement("depthStencil"),
 				MemoryLayout.PathElement.groupElement("stencil"));
diff --git a/src/notzed.xcb/classes/xcb/Connection.java b/src/notzed.xcb/classes/xcb/Connection.java
index 27ff8fc..32656d4 100644
--- a/src/notzed.xcb/classes/xcb/Connection.java
+++ b/src/notzed.xcb/classes/xcb/Connection.java
@@ -1,19 +1,19 @@
 
 package xcb;
 
-import jdk.incubator.foreign.*;
+import java.lang.foreign.*;
 import au.notzed.nativez.Pointer;
 
 /* small hack to get it to compile, unimplemented functions so far */
 
 public class Connection implements Pointer {
-	NativeSymbol symbol;
+	MemorySegment symbol;
 
-	public Connection(MemoryAddress addr, ResourceScope scope) {
-		symbol = NativeSymbol.ofAddress("Connection", addr, scope);
+	public Connection(MemoryAddress addr, MemorySession scope) {
+		symbol = MemorySegment.ofAddress(addr, 0, scope);
 	}
 
-	public static Connection create(MemoryAddress addr, ResourceScope scope) {
+	public static Connection create(MemoryAddress addr, MemorySession scope) {
 		return new Connection(addr, scope);
 	}
 
@@ -21,8 +21,8 @@ public class Connection implements Pointer {
 		return symbol.address();
 	}
 
-	public ResourceScope scope() {
-		return symbol.scope();
+	public MemorySession scope() {
+		return symbol.session();
 	}
 
 }