Browse Source

Merge pull request #3740 from laytan/wgpu

Add `vendor:wgpu`
gingerBill 1 year ago
parent
commit
7184792f7a

+ 1 - 1
.gitignore

@@ -303,7 +303,7 @@ bin/
 # - Linux/MacOS
 odin
 !odin/
-odin.dSYM
+**/*.dSYM
 *.bin
 demo.bin
 libLLVM*.so*

+ 4 - 0
core/sys/darwin/Foundation/NSWindow.odin

@@ -712,3 +712,7 @@ Window_setDelegate :: proc "c" (self: ^Window, delegate: ^WindowDelegate) {
 Window_backingScaleFactor :: proc "c" (self: ^Window) -> Float {
 	return msgSend(Float, self, "backingScaleFactor")
 }
+@(objc_type=Window, objc_name="setWantsLayer")
+Window_setWantsLayer :: proc "c" (self: ^Window, ok: BOOL) {
+	msgSend(nil, self, "setWantsLayer:", ok)
+}

+ 15 - 11
vendor/glfw/native_linux.odin

@@ -2,14 +2,18 @@
 
 package glfw
 
-// TODO: Native Linux
-// Display* glfwGetX11Display(void);
-// RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor);
-// RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
-// Window glfwGetX11Window(GLFWwindow* window);
-// void glfwSetX11SelectionString(const char* string);
-// const char* glfwGetX11SelectionString(void);
-
-// struct wl_display* glfwGetWaylandDisplay(void);
-// struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor);
-// struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window);
+import "vendor:x11/xlib"
+
+@(default_calling_convention="c", link_prefix="glfw")
+foreign {
+	GetX11Display :: proc() -> ^xlib.Display ---
+	GetX11Window :: proc(window:  WindowHandle) -> xlib.Window ---
+	GetX11Adapter :: proc(monitor: MonitorHandle) -> xlib.RRCrtc ---
+	GetX11Monitor :: proc(monitor: MonitorHandle) -> xlib.RROutput ---
+	SetX11SelectionString :: proc(string:  cstring) ---
+	GetX11SelectionString :: proc() -> cstring ---
+
+	GetWaylandDisplay :: proc()                       -> rawptr /* struct wl_display* */ ---
+	GetWaylandWindow  :: proc(window:  WindowHandle)  -> rawptr /* struct wl_surface* */ ---
+	GetWaylandMonitor :: proc(monitor: MonitorHandle) -> rawptr /* struct wl_output*  */ ---
+}

+ 37 - 7
vendor/wasm/js/runtime.js

@@ -96,6 +96,10 @@ class WasmMemoryInterface {
 	};
 	loadPtr(addr) { return this.loadU32(addr); }
 
+	loadB32(addr) {
+		return this.loadU32(addr) != 0;
+	}
+
 	loadBytes(ptr, len) {
 		return new Uint8Array(this.memory.buffer, ptr, Number(len));
 	}
@@ -104,6 +108,16 @@ class WasmMemoryInterface {
 		const bytes = this.loadBytes(ptr, Number(len));
 		return new TextDecoder().decode(bytes);
 	}
+ 
+	loadCstring(ptr) {
+		const start = this.loadPtr(ptr);
+		if (start == 0) {
+			return null;
+		}
+		let len = 0;
+		for (; this.mem.getUint8(start+len) != 0; len += 1) {}
+		return this.loadString(start, len);
+	}
 
 	storeU8(addr, value)  { this.mem.setUint8  (addr, value); }
 	storeI8(addr, value)  { this.mem.setInt8   (addr, value); }
@@ -1245,7 +1259,7 @@ class WebGLInterface {
 };
 
 
-function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
+function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
 	const MAX_INFO_CONSOLE_LINES = 512;
 	let infoConsoleLines = new Array();
 	let currentLine = {};
@@ -1366,8 +1380,15 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
 	let event_temp_data = {};
 
 	let webglContext = new WebGLInterface(wasmMemoryInterface);
+	
+	const env = {};
+
+	if (memory) {
+		env.memory = memory;
+	}
+
 	return {
-		"env": {},
+		env,
 		"odin_env": {
 			write: (fd, ptr, len) => {
 				const str = wasmMemoryInterface.loadString(ptr, len);
@@ -1720,13 +1741,16 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
  * @param {string} wasmPath                          - Path to the WASM module to run
  * @param {?HTMLPreElement} consoleElement           - Optional console/pre element to append output to, in addition to the console
  * @param {any} extraForeignImports                  - Imports, in addition to the default runtime to provide the module
+ * @param {?WasmMemoryInterface} wasmMemoryInterface - Optional memory to use instead of the defaults
  * @param {?int} intSize                             - Size (in bytes) of the integer type, should be 4 on `js_wasm32` and 8 on `js_wasm64p32`
  */
-async function runWasm(wasmPath, consoleElement, extraForeignImports, intSize = 4) {
-	const wasmMemoryInterface = new WasmMemoryInterface();
+async function runWasm(wasmPath, consoleElement, extraForeignImports, wasmMemoryInterface, intSize = 4) {
+	if (!wasmMemoryInterface) {
+		wasmMemoryInterface = new WasmMemoryInterface();
+	}
 	wasmMemoryInterface.setIntSize(intSize);
 
-	let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement);
+	let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement, wasmMemoryInterface.memory);
 	let exports = {};
 
 	if (extraForeignImports !== undefined) {
@@ -1741,11 +1765,17 @@ async function runWasm(wasmPath, consoleElement, extraForeignImports, intSize =
 	const wasm = await WebAssembly.instantiate(file, imports);
 	exports = wasm.instance.exports;
 	wasmMemoryInterface.setExports(exports);
-	wasmMemoryInterface.setMemory(exports.memory);
+
+	if (exports.memory) {
+		if (wasmMemoryInterface.memory) {
+			console.warn("WASM module exports memory, but `runWasm` was given an interface with existing memory too");
+		}
+		wasmMemoryInterface.setMemory(exports.memory);
+	}
 
 	exports._start();
 
-	// Define a `@export step :: proc(dt: f32) -> (continue: bool) {`
+	// Define a `@export step :: proc(dt: f32) -> (keep_going: bool) {`
 	// in your app and it will get called every frame.
 	// return `false` to stop the execution of the module.
 	if (exports.step) {

+ 9 - 0
vendor/wgpu/.gitignore

@@ -0,0 +1,9 @@
+lib/*
+!lib/.gitkeep
+example/web/triangle.wasm
+example/web/wgpu.js
+example/web/runtime.js
+example/example
+example/example.exe
+example/triangle
+example/triangle.exe

+ 48 - 0
vendor/wgpu/README.md

@@ -0,0 +1,48 @@
+# WGPU
+
+A cross-platform (and WASM) GPU API.
+
+WASM support is achieved by providing wrappers around the browser native WebGPU API
+that are called instead of the [wgpu-native](https://github.com/gfx-rs/wgpu-native) library,
+the wgpu-native library provides support for all other targets.
+
+Have a look at the `example/` directory for the rendering of a basic triangle.
+
+## Getting the wgpu-native libraries
+
+For native support (not the browser), some libraries are required. Fortunately this is
+extremely easy, just download them from the [releases on GitHub](https://github.com/gfx-rs/wgpu-native/releases/tag/v0.19.4.1),
+the bindings are for v0.19.4.1 at the moment.
+
+These are expected in the `lib` folder under the same name as they are released (just unzipped).
+By default it will look for a static release version (`wgpu-OS-ARCH-release.a|lib`),
+you can set `-define:WGPU_DEBUG=true` for it to look for a debug version,
+and use `-define:WGPU_SHARED=true` to look for the shared libraries.
+
+## WASM
+
+For WASM, the module has to be built with a function table to enable callbacks.
+You can do so using `-extra-linker-flags:"--export-table"`.
+
+Being able to allocate is also required (for some auxiliary APIs but also for mapping/unmapping buffers).
+
+You can set the context that is used for allocations by setting the global variable `wpgu.g_context`.
+It will default to the `runtime.default_context`.
+
+Again, have a look at the `example/` and how it is set up, doing the `--import-memory` and the likes
+is not strictly necessary but allows your app more memory than the minimal default.
+
+The bindings work on both `-target:js_wasm32` and `-target:js_wasm64p32`.
+
+## GLFW Glue
+
+There is an inner package `glfwglue` that can be used to glue together WGPU and GLFW.
+It exports one procedure `GetSurface(wgpu.Instance, glfw.WindowHandle) -> glfw.Surface`.
+The procedure will call the needed target specific procedures and return a surface configured
+for the given window.
+
+To support Wayland on Linux, you need to have GLFW compiled to support it, and use
+`-define:WGPU_GFLW_GLUE_SUPPORT_WAYLAND=true` to enable the package to check for Wayland.
+
+Do note that wgpu does not require GLFW, you can use native windows or another windowing library too.
+For that you can take inspiration from `glfwglue` on glueing them together.

+ 17 - 0
vendor/wgpu/example/Makefile

@@ -0,0 +1,17 @@
+FILES := $(wildcard *)
+
+# NOTE: changing this requires changing the same values in the `web/index.html`.
+INITIAL_MEMORY_PAGES := 2000
+MAX_MEMORY_PAGES     := 65536
+
+PAGE_SIZE := 65536
+INITIAL_MEMORY_BYTES := $(shell expr $(INITIAL_MEMORY_PAGES) \* $(PAGE_SIZE))
+MAX_MEMORY_BYTES     := $(shell expr $(MAX_MEMORY_PAGES) \* $(PAGE_SIZE))
+
+web/triangle.wasm: $(FILES) ../wgpu.js ../../wasm/js/runtime.js
+	odin build . \
+		-target:js_wasm32 -out:web/triangle.wasm -o:size \
+        -extra-linker-flags:"--export-table --import-memory --initial-memory=$(INITIAL_MEMORY_BYTES) --max-memory=$(MAX_MEMORY_BYTES)"
+
+	cp ../wgpu.js web/wgpu.js
+	cp ../../wasm/js/runtime.js web/runtime.js

+ 12 - 0
vendor/wgpu/example/build.bat

@@ -0,0 +1,12 @@
+REM NOTE: changing this requires changing the same values in the `web/index.html`.
+set INITIAL_MEMORY_PAGES=2000
+set MAX_MEMORY_PAGES=65536
+
+set PAGE_SIZE=65536
+set /a INITIAL_MEMORY_BYTES=%INITIAL_MEMORY_PAGES% * %PAGE_SIZE%
+set /a MAX_MEMORY_BYTES=%MAX_MEMORY_PAGES% * %PAGE_SIZE%
+
+call odin.exe build . -target:js_wasm32 -out:web/triangle.wasm -o:size -extra-linker-flags:"--export-table --import-memory --initial-memory=%INITIAL_MEMORY_BYTES% --max-memory=%MAX_MEMORY_BYTES%"
+
+copy "..\wgpu.js" "web\wgpu.js"
+copy "..\..\wasm\js\runtime.js" "web\runtime.js"

+ 187 - 0
vendor/wgpu/example/main.odin

@@ -0,0 +1,187 @@
+package vendor_wgpu_example_triangle
+
+import "base:runtime"
+
+import "core:fmt"
+
+import "vendor:wgpu"
+
+State :: struct {
+	ctx: runtime.Context,
+	os:  OS,
+
+	instance:        wgpu.Instance,
+	surface:         wgpu.Surface,
+	adapter:         wgpu.Adapter,
+	device:          wgpu.Device,
+	config:          wgpu.SurfaceConfiguration,
+	queue:           wgpu.Queue,
+	module:          wgpu.ShaderModule,
+	pipeline_layout: wgpu.PipelineLayout,
+	pipeline:        wgpu.RenderPipeline,
+}
+
+@(private="file")
+state: State
+
+main :: proc() {
+	state.ctx = context
+
+	os_init(&state.os)
+
+	state.instance = wgpu.CreateInstance(nil)
+	if state.instance == nil {
+		panic("WebGPU is not supported")
+	}
+	state.surface = os_get_surface(&state.os, state.instance)
+
+	wgpu.InstanceRequestAdapter(state.instance, &{ compatibleSurface = state.surface }, on_adapter, nil)
+
+	on_adapter :: proc "c" (status: wgpu.RequestAdapterStatus, adapter: wgpu.Adapter, message: cstring, userdata: rawptr) {
+		context = state.ctx
+		if status != .Success || adapter == nil {
+			fmt.panicf("request adapter failure: [%v] %s", status, message)
+		}
+		state.adapter = adapter
+		wgpu.AdapterRequestDevice(adapter, nil, on_device)
+	}
+
+	on_device :: proc "c" (status: wgpu.RequestDeviceStatus, device: wgpu.Device, message: cstring, userdata: rawptr) {
+		context = state.ctx
+		if status != .Success || device == nil {
+			fmt.panicf("request device failure: [%v] %s", status, message)
+		}
+		state.device = device 
+
+		width, height := os_get_render_bounds(&state.os)
+
+		state.config = wgpu.SurfaceConfiguration {
+			device      = state.device,
+			usage       = { .RenderAttachment },
+			format      = .BGRA8Unorm,
+			width       = width,
+			height      = height,
+			presentMode = .Fifo,
+			alphaMode   = .Opaque,
+		}
+		wgpu.SurfaceConfigure(state.surface, &state.config)
+
+		state.queue = wgpu.DeviceGetQueue(state.device)
+
+		shader :: `
+	@vertex
+	fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
+		let x = f32(i32(in_vertex_index) - 1);
+		let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
+		return vec4<f32>(x, y, 0.0, 1.0);
+	}
+
+	@fragment
+	fn fs_main() -> @location(0) vec4<f32> {
+		return vec4<f32>(1.0, 0.0, 0.0, 1.0);
+	}`
+
+		state.module = wgpu.DeviceCreateShaderModule(state.device, &{
+			nextInChain = &wgpu.ShaderModuleWGSLDescriptor{
+				sType = .ShaderModuleWGSLDescriptor,
+				code  = shader,
+			},
+		})
+
+		state.pipeline_layout = wgpu.DeviceCreatePipelineLayout(state.device, &{})
+		state.pipeline = wgpu.DeviceCreateRenderPipeline(state.device, &{
+			layout = state.pipeline_layout,
+			vertex = {
+				module     = state.module,
+				entryPoint = "vs_main",
+			},
+			fragment = &{
+				module      = state.module,
+				entryPoint  = "fs_main",
+				targetCount = 1,
+				targets     = &wgpu.ColorTargetState{
+					format    = .BGRA8Unorm,
+					writeMask = wgpu.ColorWriteMaskFlags_All,
+				},
+			},
+			primitive = {
+				topology = .TriangleList,
+
+			},
+			multisample = {
+				count = 1,
+				mask  = 0xFFFFFFFF,
+			},
+		})
+
+		os_run(&state.os)
+	}
+}
+
+resize :: proc "c" () {
+	context = state.ctx
+
+	state.config.width, state.config.height = os_get_render_bounds(&state.os)
+	wgpu.SurfaceConfigure(state.surface, &state.config)
+}
+
+frame :: proc "c" (dt: f32) {
+	context = state.ctx
+
+	surface_texture := wgpu.SurfaceGetCurrentTexture(state.surface)
+	switch surface_texture.status {
+	case .Success:
+		// All good, could check for `surface_texture.suboptimal` here.
+	case .Timeout, .Outdated, .Lost:
+		// Skip this frame, and re-configure surface.
+		if surface_texture.texture != nil {
+			wgpu.TextureRelease(surface_texture.texture)
+		}
+		resize()
+		return
+	case .OutOfMemory, .DeviceLost:
+		// Fatal error
+		fmt.panicf("[triangle] get_current_texture status=%v", surface_texture.status)
+	}
+	defer wgpu.TextureRelease(surface_texture.texture)
+
+	frame := wgpu.TextureCreateView(surface_texture.texture, nil)
+	defer wgpu.TextureViewRelease(frame)
+
+	command_encoder := wgpu.DeviceCreateCommandEncoder(state.device, nil)
+	defer wgpu.CommandEncoderRelease(command_encoder)
+
+	render_pass_encoder := wgpu.CommandEncoderBeginRenderPass(
+		command_encoder, &{
+			colorAttachmentCount = 1,
+			colorAttachments     = &wgpu.RenderPassColorAttachment{
+				view       = frame,
+				loadOp     = .Clear,
+				storeOp    = .Store,
+				clearValue = { r = 0, g = 1, b = 0, a = 1 },
+			},
+		},
+	)
+	defer wgpu.RenderPassEncoderRelease(render_pass_encoder)
+
+	wgpu.RenderPassEncoderSetPipeline(render_pass_encoder, state.pipeline)
+	wgpu.RenderPassEncoderDraw(render_pass_encoder, vertexCount=3, instanceCount=1, firstVertex=0, firstInstance=0)
+	wgpu.RenderPassEncoderEnd(render_pass_encoder)
+
+	command_buffer := wgpu.CommandEncoderFinish(command_encoder, nil)
+	defer wgpu.CommandBufferRelease(command_buffer)
+
+	wgpu.QueueSubmit(state.queue, { command_buffer })
+	wgpu.SurfacePresent(state.surface)
+}
+
+finish :: proc() {
+	wgpu.RenderPipelineRelease(state.pipeline)
+	wgpu.PipelineLayoutRelease(state.pipeline_layout)
+	wgpu.ShaderModuleRelease(state.module)
+	wgpu.QueueRelease(state.queue)
+	wgpu.DeviceRelease(state.device)
+	wgpu.AdapterRelease(state.adapter)
+	wgpu.SurfaceRelease(state.surface)
+	wgpu.InstanceRelease(state.instance)
+}

+ 55 - 0
vendor/wgpu/example/os_glfw.odin

@@ -0,0 +1,55 @@
+//+build !js
+package vendor_wgpu_example_triangle
+
+import "core:time"
+
+import "vendor:glfw"
+import "vendor:wgpu"
+import "vendor:wgpu/glfwglue"
+
+OS :: struct {
+	window: glfw.WindowHandle,
+}
+
+os_init :: proc(os: ^OS) {
+	if !glfw.Init() {
+		panic("[glfw] init failure")
+	}
+
+	glfw.WindowHint(glfw.CLIENT_API, glfw.NO_API)
+	os.window = glfw.CreateWindow(960, 540, "WGPU Native Triangle", nil, nil)
+
+	glfw.SetFramebufferSizeCallback(os.window, size_callback)
+}
+
+os_run :: proc(os: ^OS) {
+    dt: f32
+
+	for !glfw.WindowShouldClose(os.window) {
+		start := time.tick_now()
+
+		glfw.PollEvents()
+		frame(dt)
+
+		dt = f32(time.duration_seconds(time.tick_since(start)))
+	}
+
+	finish()
+
+	glfw.DestroyWindow(os.window)
+	glfw.Terminate()
+}
+
+os_get_render_bounds :: proc(os: ^OS) -> (width, height: u32) {
+	iw, ih := glfw.GetWindowSize(os.window)
+	return u32(iw), u32(ih)
+}
+
+os_get_surface :: proc(os: ^OS, instance: wgpu.Instance) -> wgpu.Surface {
+	return glfwglue.GetSurface(instance, os.window)
+}
+
+@(private="file")
+size_callback :: proc "c" (window: glfw.WindowHandle, width, height: i32) {
+	resize()
+}

+ 60 - 0
vendor/wgpu/example/os_js.odin

@@ -0,0 +1,60 @@
+package vendor_wgpu_example_triangle
+
+import "vendor:wgpu"
+import "vendor:wasm/js"
+
+OS :: struct {
+	initialized: bool,
+}
+
+@(private="file")
+g_os: ^OS
+
+os_init :: proc(os: ^OS) {
+	g_os = os
+	assert(js.add_window_event_listener(.Resize, nil, size_callback))
+}
+
+// NOTE: frame loop is done by the runtime.js repeatedly calling `step`.
+os_run :: proc(os: ^OS) {
+	os.initialized = true
+}
+
+os_get_render_bounds :: proc(os: ^OS) -> (width, height: u32) {
+	rect := js.get_bounding_client_rect("body")
+	return u32(rect.width), u32(rect.height)
+}
+
+os_get_surface :: proc(os: ^OS, instance: wgpu.Instance) -> wgpu.Surface {
+	return wgpu.InstanceCreateSurface(
+		instance,
+		&wgpu.SurfaceDescriptor{
+			nextInChain = &wgpu.SurfaceDescriptorFromCanvasHTMLSelector{
+				sType = .SurfaceDescriptorFromCanvasHTMLSelector,
+				selector = "#wgpu-canvas",
+			},
+		},
+	)
+}
+
+@(private="file", export)
+step :: proc(dt: f32) -> bool {
+	if !g_os.initialized {
+		return true
+	}
+
+	frame(dt)
+	return true
+}
+
+@(private="file", fini)
+os_fini :: proc() {
+	js.remove_window_event_listener(.Resize, nil, size_callback)
+
+	finish()
+}
+
+@(private="file")
+size_callback :: proc(e: js.Event) {
+	resize()
+}

+ 23 - 0
vendor/wgpu/example/web/index.html

@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en" style="height: 100%;">
+	<head>
+		<meta charset="UTF-8">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<title>WGPU WASM Triangle</title>
+	</head>
+	<body id="body" style="height: 100%; padding: 0; margin: 0; overflow: hidden;">
+		<canvas id="wgpu-canvas"></canvas>
+	
+		<script type="text/javascript" src="runtime.js"></script>
+		<script type="text/javascript" src="wgpu.js"></script>
+		<script type="text/javascript">
+			const mem = new WebAssembly.Memory({ initial: 2000, maximum: 65536, shared: false });
+			const memInterface = new odin.WasmMemoryInterface();
+			memInterface.setMemory(mem);
+
+			const wgpuInterface = new odin.WebGPUInterface(memInterface);
+
+			odin.runWasm("triangle.wasm", null, { wgpu: wgpuInterface.getInterface() }, memInterface, /*intSize=8*/);
+		</script>
+	</body>
+</html>

+ 6 - 0
vendor/wgpu/glfwglue/glue.odin

@@ -0,0 +1,6 @@
+//+build !linux
+//+build !windows
+//+build !darwin
+package wgpu_glfw_glue
+
+#panic("package wgpu/glfwglue is not supported on the current target")

+ 23 - 0
vendor/wgpu/glfwglue/glue_darwin.odin

@@ -0,0 +1,23 @@
+package wgpu_glfw_glue
+
+import    "vendor:glfw"
+import    "vendor:wgpu"
+import CA "vendor:darwin/QuartzCore"
+
+GetSurface :: proc(instance: wgpu.Instance, window: glfw.WindowHandle) -> wgpu.Surface {
+	ns_window := glfw.GetCocoaWindow(window)
+	ns_window->contentView()->setWantsLayer(true)
+	metal_layer := CA.MetalLayer_layer()
+	ns_window->contentView()->setLayer(metal_layer)
+	return wgpu.InstanceCreateSurface(
+		instance,
+		&wgpu.SurfaceDescriptor{
+			nextInChain = &wgpu.SurfaceDescriptorFromMetalLayer{
+				chain = wgpu.ChainedStruct{
+					sType = .SurfaceDescriptorFromMetalLayer,
+				},
+				layer = rawptr(metal_layer),
+			},
+		},
+	)
+}

+ 43 - 0
vendor/wgpu/glfwglue/glue_linux.odin

@@ -0,0 +1,43 @@
+package wgpu_glfw_glue
+
+import "vendor:glfw"
+import "vendor:wgpu"
+
+// GLFW needs to be compiled with wayland support for this to work.
+SUPPORT_WAYLAND :: #config(WGPU_GFLW_GLUE_SUPPORT_WAYLAND, false)
+
+GetSurface :: proc(instance: wgpu.Instance, window: glfw.WindowHandle) -> wgpu.Surface {
+	when SUPPORT_WAYLAND {
+		if glfw.GetPlatform() == glfw.PLATFORM_WAYLAND {
+			display := glfw.GetWaylandDisplay()
+			surface := glfw.GetWaylandWindow(window)
+			return wgpu.InstanceCreateSurface(
+				instance,
+				&wgpu.SurfaceDescriptor{
+					nextInChain = &wgpu.SurfaceDescriptorFromWaylandSurface{
+						chain = {
+							sType = .SurfaceDescriptorFromWaylandSurface,
+						},
+						display = display,
+						surface = surface,
+					},
+				},
+			)
+		}
+	}
+
+	display := glfw.GetX11Display()
+	window  := glfw.GetX11Window(window)
+	return wgpu.InstanceCreateSurface(
+		instance,
+		&wgpu.SurfaceDescriptor{
+			nextInChain = &wgpu.SurfaceDescriptorFromXlibWindow{
+				chain = {
+					sType = .SurfaceDescriptorFromXlibWindow,
+				},
+				display = display,
+				window  = u64(window),
+			},
+		},
+	)
+}

+ 23 - 0
vendor/wgpu/glfwglue/glue_windows.odin

@@ -0,0 +1,23 @@
+package wgpu_glfw_glue
+
+import win "core:sys/windows"
+
+import     "vendor:glfw"
+import     "vendor:wgpu"
+
+GetSurface :: proc(instance: wgpu.Instance, window: glfw.WindowHandle) -> wgpu.Surface {
+	hwnd      := glfw.GetWin32Window(window)
+	hinstance := win.GetModuleHandleW(nil)
+	return wgpu.InstanceCreateSurface(
+		instance,
+		&wgpu.SurfaceDescriptor{
+			nextInChain = &wgpu.SurfaceDescriptorFromWindowsHWND{
+				chain = wgpu.ChainedStruct{
+					sType = .SurfaceDescriptorFromWindowsHWND,
+				},
+				hinstance = rawptr(hinstance),
+				hwnd = rawptr(hwnd),
+			},
+		},
+	)
+}

+ 0 - 0
vendor/wgpu/lib/.gitkeep


+ 2916 - 0
vendor/wgpu/wgpu.js

@@ -0,0 +1,2916 @@
+(function() {
+
+/**
+ * Assumptions:
+ * - Ability to allocate memory, set the context to allocate with using the global `wgpu.g_context`
+ * - Exports a function table (for callbacks), added with `-extra-linker-flags:"--export-table"`
+ */
+class WebGPUInterface {
+
+	/**
+	 * @param {WasmMemoryInterface} mem
+	 */
+	constructor(mem) {
+		this.mem = mem;
+
+		this.enums = {
+			FeatureName: [undefined, "depth-clip-control", "depth32float-stencil8", "timestamp-query", "texture-compression-bc", "texture-compression-etc2", "texture-compression-astc", "indirect-first-instance", "shader-f16", "rg11b10ufloat-renderable", "bgra8unorm-storage", "float32-filterable", ],
+			StoreOp: [undefined, "store", "discard", ],
+			LoadOp: [undefined, "clear", "load", ],
+			BufferBindingType: [undefined, "uniform", "storage", "read-only-storage", ],
+			SamplerBindingType: [undefined, "filtering", "non-filtering", "comparison", ],
+			TextureSampleType: [undefined, "float", "unfilterable-float", "depth", "sint", "uint", ],
+			TextureViewDimension: [undefined, "1d", "2d", "2d-array", "cube", "cube-array", "3d", ],
+			StorageTextureAccess: [undefined, "write-only", "read-only", "read-write", ],
+			TextureFormat: [undefined, "r8unorm", "r8snorm", "r8uint", "r8sint", "r16uint", "r16sint", "r16float", "rg8unorm", "rg8snorm", "rg8uint", "rg8sint", "r32float", "r32uint", "r32sint", "rg16uint", "rg16sint", "rg16float", "rgba8unorm", "rgba8unorm-srgb", "rgba8snorm", "rgba8uint", "rgba8sint", "bgra8unorm", "bgra8unorm-srgb", "rgb10a2uint", "rgb10a2unorm", "rg11b10ufloat", "rgb9e5ufloat", "rg32float", "rg32uint", "rg32sint", "rgba16uint", "rgba16sint", "rgba16float", "rgba32float", "rgba32uint", "rgba32sint", "stencil8", "depth16unorm", "depth24plus", "depth24plus-stencil8", "depth32float", "depth32float-stencil8", "bc1-rgba-unorm", "bc1-rgba-unorm-srgb", "bc2-rgba-unorm", "bc2-rgba-unorm-srgb", "bc3-rgba-unorm", "bc3-rgba-unorm-srgb", "bc4-r-unorm", "bc4-r-snorm", "bc5-rg-unorm", "bc5-rg-snorm", "bc6h-rgb-ufloat", "bc6h-rgb-float", "bc7-rgba-unorm", "bc7-rgba-unorm-srgb", "etc2-rgb8unorm", "etc2-rgb8unorm-srgb", "etc2-rgb8a1unorm", "etc2-rgb8a1unorm-srgb", "etc2-rgba8unorm", "etc2-rgba8unorm-srgb", "eac-r11unorm", "eac-r11snorm", "eac-rg11unorm", "eac-rg11snorm", "astc-4x4-unorm", "astc-4x4-unorm-srgb", "astc-5x4-unorm", "astc-5x4-unorm-srgb", "astc-5x5-unorm", "astc-5x5-unorm-srgb", "astc-6x5-unorm", "astc-6x5-unorm-srgb", "astc-6x6-unorm", "astc-6x6-unorm-srgb", "astc-8x5-unorm", "astc-8x5-unorm-srgb", "astc-8x6-unorm", "astc-8x6-unorm-srgb", "astc-8x8-unorm", "astc-8x8-unorm-srgb", "astc-10x5-unorm", "astc-10x5-unorm-srgb", "astc-10x6-unorm", "astc-10x6-unorm-srgb", "astc-10x8-unorm", "astc-10x8-unorm-srgb", "astc-10x10-unorm", "astc-10x10-unorm-srgb", "astc-12x10-unorm", "astc-12x10-unorm-srgb", "astc-12x12-unorm", "astc-12x12-unorm-srgb", ],
+			QueryType: ["occlusion", "timestamp", ],
+			VertexStepMode: ["vertex", "instance", "vertex-buffer-not-used", ],
+			VertexFormat: [undefined, "uint8x2", "uint8x4", "sint8x2", "sint8x4", "unorm8x2", "unorm8x4", "snorm8x2", "snorm8x4", "uint16x2", "uint16x4", "sint16x2", "sint16x4", "unorm16x2", "unorm16x4", "snorm16x2", "snorm16x4", "float16x2", "float16x4", "float32", "float32x2", "float32x3", "float32x4", "uint32", "uint32x2", "uint32x3", "uint32x4", "sint32", "sint32x2", "sint32x3", "sint32x4", ],
+			PrimitiveTopology: ["point-list", "line-list", "line-strip", "triangle-list", "triangle-strip", ],
+			IndexFormat: [undefined, "uint16", "uint32", ],
+			FrontFace: ["ccw", "cw", ],
+			CullMode: ["none", "front", "back", ],
+			AddressMode: ["repeat", "mirror-repeat", "clamp-to-edge", ],
+			FilterMode: ["nearest", "linear", ],
+			MipmapFilterMode: ["nearest", "linear", ],
+			CompareFunction: [undefined, "never", "less", "less-equal", "greater", "greater-equal", "equal", "not-equal", "always", ],
+			TextureDimension: ["1d", "2d", "3d", ],
+			ErrorType: ["no-error", "validation", "out-of-memory", "internal", "unknown", "device-lost", ],
+			WGSLFeatureName: [undefined, "readonly_and_readwrite_storage_textures", "packed_4x8_integer_dot_product", "unrestricted_pointer_parameters", "pointer_composite_access", ],
+			PowerPreference: [undefined, "low-power", "high-performance", ],
+			CompositeAlphaMode: ["auto", "opaque", "premultiplied", "unpremultiplied", "inherit", ],
+			StencilOperation: ["keep", "zero", "replace", "invert", "increment-clamp", "decrement-clamp", "increment-wrap", "decrement-wrap", ],
+			BlendOperation: ["add", "subtract", "reverse-subtract", "min", "max", ],
+			BlendFactor: ["zero", "one", "src", "one-minus-src", "src-alpha", "one-minus-src-alpha", "dst", "one-minus-dst", "dst-alpha", "one-minus-dst-alpha", "src-alpha-saturated", "constant", "one-minus-constant", ],
+			PresentMode: ["fifo", "fifo-relaxed", "immediate", "mailbox", ],
+			TextureAspect: ["all", "stencil-only", "depth-only"],
+		};
+
+		/** @type {WebGPUObjectManager<{}>} */
+		this.instances = new WebGPUObjectManager("Instance", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUAdapter>} */
+		this.adapters = new WebGPUObjectManager("Adapter", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUBindGroup>} */
+		this.bindGroups = new WebGPUObjectManager("BindGroup", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUBindGroupLayout>} */
+		this.bindGroupLayouts = new WebGPUObjectManager("BindGroupLayout", this.mem);
+
+		/** @type {WebGPUObjectManager<{ buffer: GPUBuffer, mapping: ?{ range: ArrayBuffer, ptr: number, size: number } }>} */
+		this.buffers = new WebGPUObjectManager("Buffer", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUDevice>} */
+		this.devices = new WebGPUObjectManager("Device", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUCommandBuffer>} */
+		this.commandBuffers = new WebGPUObjectManager("CommandBuffer", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUCommandEncoder>} */
+		this.commandEncoders = new WebGPUObjectManager("CommandEncoder", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUComputePassEncoder>} */
+		this.computePassEncoders = new WebGPUObjectManager("ComputePassEncoder", this.mem);
+
+		/** @type {WebGPUObjectManager<GPURenderPassEncoder>} */
+		this.renderPassEncoders = new WebGPUObjectManager("RenderPassEncoder", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUQuerySet>} */
+		this.querySets = new WebGPUObjectManager("QuerySet", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUComputePipeline>} */
+		this.computePipelines = new WebGPUObjectManager("ComputePipeline", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUPipelineLayout>} */
+		this.pipelineLayouts = new WebGPUObjectManager("PipelineLayout", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUQueue>} */
+		this.queues = new WebGPUObjectManager("Queue", this.mem);
+
+		/** @type {WebGPUObjectManager<GPURenderBundle>} */
+		this.renderBundles = new WebGPUObjectManager("RenderBundle", this.mem);
+
+		/** @type {WebGPUObjectManager<GPURenderBundleEncoder>} */
+		this.renderBundleEncoders = new WebGPUObjectManager("RenderBundleEncoder", this.mem);
+
+		/** @type {WebGPUObjectManager<GPURenderPipeline>} */
+		this.renderPipelines = new WebGPUObjectManager("RenderPipeline", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUSampler>} */
+		this.samplers = new WebGPUObjectManager("Sampler", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUShaderModule>} */
+		this.shaderModules = new WebGPUObjectManager("ShaderModule", this.mem);
+
+		/** @type {WebGPUObjectManager<HTMLCanvasElement>} */
+		this.surfaces = new WebGPUObjectManager("Surface", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUTexture>} */
+		this.textures = new WebGPUObjectManager("Texture", this.mem);
+
+		/** @type {WebGPUObjectManager<GPUTextureView>} */
+		this.textureViews = new WebGPUObjectManager("TextureView", this.mem);
+	}
+
+	/**
+	 * @param {number|BigInt} src
+	 * @returns {number|BigInt}
+	 */
+	uint(src) {
+		if (this.mem.intSize == 8) {
+			return BigInt(src);
+		} else if (this.mem.intSize == 4) {
+			return src;
+		} else {
+			throw new Error("unreachable");
+		}
+	}
+
+	/**
+	 * @param {number|BigInt} src
+	 * @returns {number}
+	 */
+	unwrapBigInt(src) {
+		if (typeof src == "number") {
+			return src;
+		}
+
+		const MAX_SAFE_INTEGER = 9007199254740991n;
+		if (typeof src != "bigint") {
+			throw new TypeError(`unwrapBigInt got invalid param of type ${typeof src}`);
+		}
+
+		if (src > MAX_SAFE_INTEGER) {
+			throw new Error(`unwrapBigInt precision would be lost converting ${src}`);
+		}
+
+		return Number(src);
+	}
+
+	/**
+	 * @param {boolean} condition
+	 * @param {string} message
+	 */
+	assert(condition, message = "assertion failure") {
+		if (!condition) {
+			throw new Error(message);
+		}
+	}
+
+	/**
+	 * @template T
+	 *
+	 * @param {number} count
+	 * @param {number} start
+	 * @param {function(number): T} decoder
+	 * @param {number} stride
+	 * @returns {Array<T>}
+	 */
+	array(count, start, decoder, stride) {
+		if (count == 0) {
+			return [];
+		}
+		this.assert(start != 0);
+
+		const out = [];
+		for (let i = 0; i < count; i += 1) {
+			out.push(decoder.call(this, start));
+			start += stride;
+		}
+		return out;
+	}
+
+	/**
+	 * @param {string} name
+	 * @param {number} ptr
+	 * @returns {`GPU${name}`}
+	 */
+	enumeration(name, ptr) {
+		const int = this.mem.loadI32(ptr);
+		this.assert(this.enums[name], `Unknown enumeration "${name}"`);
+		return this.enums[name][int];
+	}
+
+	/**
+	 * @param {GPUSupportedFeatures} features
+	 * @param {number} ptr
+	 * @returns {BigInt|number}
+	 */
+	genericEnumerateFeatures(features, ptr) {
+		const availableFeatures = [];
+		this.enums.FeatureName.forEach((feature, value) => {
+			if (!feature) {
+				return;
+			}
+
+			if (features.has(feature)) {
+				availableFeatures.push(value);
+			}
+		});
+
+		if (ptr != 0) {
+			for (let i = 0; i < availableFeatures.length; i += 1) {
+				this.mem.storeI32(ptr + (i * 4), availableFeatures[i]);
+			}
+		}
+
+		return this.uint(availableFeatures.length);
+	}
+
+	/**
+	 * @param {GPUSupportedLimits} limits
+	 * @param {number} ptr
+	 */
+	genericGetLimits(limits, supportedLimitsPtr) {
+		this.assert(supportedLimitsPtr != 0);
+		const limitsPtr = supportedLimitsPtr + 8;
+
+		this.mem.storeU32(limitsPtr + 0,   limits.maxTextureDimension1D);
+		this.mem.storeU32(limitsPtr + 4,   limits.maxTextureDimension2D);
+		this.mem.storeU32(limitsPtr + 8,   limits.maxTextureDimension3D);
+		this.mem.storeU32(limitsPtr + 12,  limits.maxTextureArrayLayers);
+		this.mem.storeU32(limitsPtr + 16,  limits.maxBindGroups);
+		this.mem.storeU32(limitsPtr + 20,  limits.maxBindGroupsPlusVertexBuffers);
+		this.mem.storeU32(limitsPtr + 24,  limits.maxBindingsPerBindGroup);
+		this.mem.storeU32(limitsPtr + 28,  limits.maxDynamicUniformBuffersPerPipelineLayout);
+		this.mem.storeU32(limitsPtr + 32,  limits.maxDynamicStorageBuffersPerPipelineLayout);
+		this.mem.storeU32(limitsPtr + 36,  limits.maxSampledTexturesPerShaderStage);
+		this.mem.storeU32(limitsPtr + 40,  limits.maxSamplersPerShaderStage);
+		this.mem.storeU32(limitsPtr + 44,  limits.maxStorageBuffersPerShaderStage);
+		this.mem.storeU32(limitsPtr + 48,  limits.maxStorageTexturesPerShaderStage);
+		this.mem.storeU32(limitsPtr + 52,  limits.maxUniformBuffersPerShaderStage);
+		this.mem.storeU64(limitsPtr + 56,  limits.maxUniformBufferBindingSize);
+		this.mem.storeU64(limitsPtr + 64,  limits.maxStorageBufferBindingSize);
+		this.mem.storeU32(limitsPtr + 72,  limits.minUniformBufferOffsetAlignment);
+		this.mem.storeU32(limitsPtr + 76,  limits.minStorageBufferOffsetAlignment);
+		this.mem.storeU32(limitsPtr + 80,  limits.maxVertexBuffers);
+		this.mem.storeU64(limitsPtr + 88,  limits.maxBufferSize);
+		this.mem.storeU32(limitsPtr + 96,  limits.maxVertexAttributes);
+		this.mem.storeU32(limitsPtr + 100, limits.maxVertexBufferArrayStride);
+		this.mem.storeU32(limitsPtr + 104, limits.maxInterStageShaderComponents);
+		this.mem.storeU32(limitsPtr + 108, limits.maxInterStageShaderVariables);
+		this.mem.storeU32(limitsPtr + 112, limits.maxColorAttachments);
+		this.mem.storeU32(limitsPtr + 116, limits.maxColorAttachmentBytesPerSample);
+		this.mem.storeU32(limitsPtr + 120, limits.maxComputeWorkgroupStorageSize);
+		this.mem.storeU32(limitsPtr + 124, limits.maxComputeInvocationsPerWorkgroup);
+		this.mem.storeU32(limitsPtr + 128, limits.maxComputeWorkgroupSizeX);
+		this.mem.storeU32(limitsPtr + 132, limits.maxComputeWorkgroupSizeY);
+		this.mem.storeU32(limitsPtr + 136, limits.maxComputeWorkgroupSizeZ);
+		this.mem.storeU32(limitsPtr + 140, limits.maxComputeWorkgroupsPerDimension);
+
+		return true;
+	}
+
+	/**
+	 * @param {number} ptr
+	 * @returns {GPUFeatureName}
+	 */
+	FeatureNamePtr(ptr) {
+		return this.FeatureName(this.mem.loadI32(ptr));
+	}
+
+	/**
+	 * @param {number} featureInt
+	 * @returns {GPUFeatureName}
+	 */
+	FeatureName(featureInt) {
+		return this.enums.FeatureName[featureInt];
+	}
+
+	/**
+	 * @param {number} ptr
+	 * @returns {GPUSupportedLimits}
+	 */
+	RequiredLimitsPtr(ptr) {
+		const start = this.mem.loadPtr(ptr);
+		if (start == 0) {
+			return undefined;
+		}
+
+		return this.Limits(start + 8);
+	}
+
+	/**
+	 * @param {number} start
+	 * @return {GPUSupportedLimits}
+	 */
+	Limits(start) {
+		const limitU32 = (ptr) => {
+			const value = this.mem.loadU32(ptr);
+			if (value == 0xFFFFFFFF) { // LIMIT_32_UNDEFINED.
+				return undefined;
+			}
+			return value;
+		};
+
+		const limitU64 = (ptr) => {
+			const part1 = this.mem.loadU32(ptr);
+			const part2 = this.mem.loadU32(ptr + 4);
+			if (part1 != 0xFFFFFFFF || part2 != 0xFFFFFFFF) { // LIMIT_64_UNDEFINED.
+				return this.mem.loadU64(ptr);
+			}
+			return undefined;
+		};
+
+		return {
+			maxTextureDimension1D:                     limitU32(start + 0),
+			maxTextureDimension2D:                     limitU32(start + 4),
+			maxTextureDimension3D:                     limitU32(start + 8),
+			maxTextureArrayLayers:                     limitU32(start + 12),
+			maxBindGroups:                             limitU32(start + 16),
+			maxBindGroupsPlusVertexBuffers:            limitU32(start + 20),
+			maxBindingsPerBindGroup:                   limitU32(start + 24),
+			maxDynamicUniformBuffersPerPipelineLayout: limitU32(start + 28),
+			maxDynamicStorageBuffersPerPipelineLayout: limitU32(start + 32),
+			maxSampledTexturesPerShaderStage:          limitU32(start + 36),
+			maxSamplersPerShaderStage:                 limitU32(start + 40),
+			maxStorageBuffersPerShaderStage:           limitU32(start + 44),
+			maxStorageTexturesPerShaderStage:          limitU32(start + 48),
+			maxUniformBuffersPerShaderStage:           limitU32(start + 52),
+			maxUniformBufferBindingSize:               limitU64(start + 56),
+			maxStorageBufferBindingSize:               limitU64(start + 64),
+			minUniformBufferOffsetAlignment:           limitU32(start + 72),
+			minStorageBufferOffsetAlignment:           limitU32(start + 76),
+			maxVertexBuffers:                          limitU32(start + 80),
+			maxBufferSize:                             limitU64(start + 88),
+			maxVertexAttributes:                       limitU32(start + 96),
+			maxVertexBufferArrayStride:                limitU32(start + 100),
+			maxInterStageShaderComponents:             limitU32(start + 104),
+			maxInterStageShaderVariables:              limitU32(start + 108),
+			maxColorAttachments:                       limitU32(start + 112),
+			maxColorAttachmentBytesPerSample:          limitU32(start + 116),
+			maxComputeWorkgroupStorageSize:            limitU32(start + 120),
+			maxComputeInvocationsPerWorkgroup:         limitU32(start + 124),
+			maxComputeWorkgroupSizeX:                  limitU32(start + 128),
+			maxComputeWorkgroupSizeY:                  limitU32(start + 132),
+			maxComputeWorkgroupSizeZ:                  limitU32(start + 136),
+			maxComputeWorkgroupsPerDimension:          limitU32(start + 140),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUQueueDescriptor}
+	 */
+	QueueDescriptor(start) {
+		return {
+			label: this.mem.loadCstring(start + 4),
+		};
+	}
+
+	/**
+	 * @param {number} ptr
+	 * @returns {GPUComputePassTimestampWrites}
+	 */
+	ComputePassTimestampWritesPtr(ptr) {
+		const start = this.mem.loadPtr(ptr);
+		if (start == 0) {
+			return undefined;
+		}
+
+		return {
+			querySet:                  this.querySets.get(this.mem.loadPtr(start + 0)),
+			beginningOfPassWriteIndex: this.mem.loadU32(start + 4),
+			endOfPassWriteIndex:       this.mem.loadU32(start + 8),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPURenderPassColorAttachment}
+	 */
+	RenderPassColorAttachment(start) {
+		const viewIdx = this.mem.loadPtr(start + 4);
+		const resolveTargetIdx = this.mem.loadPtr(start + 8);
+
+		return {
+			view: viewIdx > 0 ? this.textureViews.get(viewIdx) : undefined,
+			resolveTarget: resolveTargetIdx > 0 ? this.textureViews.get(resolveTargetIdx) : undefined,
+			loadOp: this.enumeration("LoadOp", start + 12),
+			storeOp: this.enumeration("StoreOp", start + 16),
+			clearValue: this.Color(start + 24),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUColor}
+	 */
+	Color(start) {
+		return {
+			r: this.mem.loadF64(start + 0),
+			g: this.mem.loadF64(start + 8),
+			b: this.mem.loadF64(start + 16),
+			a: this.mem.loadF64(start + 24),
+		};
+	}
+
+	/**
+	 * @param {number} ptr
+	 * @returns {GPURenderPassDepthStencilAttachment}
+	 */
+	RenderPassDepthStencilAttachmentPtr(ptr) {
+		const start = this.mem.loadPtr(ptr);
+		if (start == 0) {
+			return undefined;
+		}
+
+		return {
+			view:              this.textureViews.get(this.mem.loadPtr(start + 0)),
+			depthLoadOp:       this.enumeration("LoadOp", start + 4),
+			depthStoreOp:      this.enumeration("StoreOp", start + 8),
+			depthClearValue:   this.mem.loadF32(start + 12),
+			depthReadOnly:     this.mem.loadB32(start + 16),
+			stencilLoadOp:     this.enumeration("LoadOp", start + 20),
+			stencilStoreOp:    this.enumeration("StoreOp", start + 24),
+			stencilClearValue: this.mem.loadF32(start + 28),
+			stencilReadOnly:   this.mem.loadB32(start + 32),
+		};
+	}
+
+	/**
+	 * @param {number} ptr
+	 * @returns {undefined|GPUQuerySet}
+	 */
+	QuerySet(ptr) {
+		ptr = this.mem.loadPtr(ptr);
+		if (ptr == 0) {
+			return undefined;
+		}
+
+		return this.querySets.get(ptr);
+	}
+
+	/**
+	 * @param {number} ptr
+	 * @returns {GPURenderPassTimestampWrites}
+	 */
+	RenderPassTimestampWritesPtr(ptr) {
+		return this.ComputePassTimestampWritesPtr(ptr);
+	}
+	
+	/**
+	 * @param {number} start
+	 * @returns {GPUImageDataLayout}
+	 */
+	TextureDataLayout(start) {
+		return {
+			offset: this.mem.loadU64(start + 8),
+			bytesPerRow: this.mem.loadU32(start + 16),
+			rowsPerImage: this.mem.loadU32(start + 20),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUImageCopyBuffer}
+	 */
+	ImageCopyBuffer(start) {
+		return {
+			...this.TextureDataLayout(start + 8),
+			buffer: this.buffers.get(this.mem.loadPtr(start + 32)).buffer,
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUImageCopyTexture}
+	 */
+	ImageCopyTexture(start) {
+		return {
+			texture: this.textures.get(this.mem.loadPtr(start + 4)),
+			mipLevel: this.mem.loadU32(start + 8),
+			origin: this.Origin3D(start + 12),
+			aspect: this.enumeration("TextureAspect", start + 24),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUOrigin3D}
+	 */
+	Origin3D(start) {
+		return {
+			x: this.mem.loadU32(start + 0),
+			y: this.mem.loadU32(start + 4),
+			z: this.mem.loadU32(start + 8),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUExtent3D}
+	 */
+	Extent3D(start) {
+		return {
+			width:              this.mem.loadU32(start + 0),
+			height:             this.mem.loadU32(start + 4),
+			depthOrArrayLayers: this.mem.loadU32(start + 8),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUBindGroupEntry}
+	 */
+	BindGroupEntry(start) {
+		const buffer = this.mem.loadPtr(start + 8);
+		const sampler = this.mem.loadPtr(start + 32);
+		const textureView = this.mem.loadPtr(start + 36);
+
+		/** @type {GPUBindingResource} */
+		let resource;
+		if (buffer > 0) {
+			resource = {
+				buffer: this.buffers.get(buffer).buffer,
+				offset: this.mem.loadU64(start + 16),
+				size: this.mem.loadU64(start + 24),
+			}
+		} else if (sampler > 0) {
+			resource = this.samplers.get(sampler);
+		} else if (textureView > 0) {
+			resource = this.textureViews.get(textureView);
+		}
+
+		return {
+			binding: this.mem.loadU32(start + 4),
+			resource: resource,
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUBindGroupLayoutEntry}
+	 */
+	BindGroupLayoutEntry(start) {
+		const entry = {
+			binding:        this.mem.loadU32(start + 4),
+			visibility:     this.mem.loadU32(start + 8),
+			buffer:         this.BufferBindingLayout(start + 16),
+			sampler:        this.SamplerBindingLayout(start + 40),
+			texture:        this.TextureBindingLayout(start + 48),
+			storageTexture: this.StorageTextureBindingLayout(start + 64),
+		};
+		if (!entry.buffer.type) {
+			entry.buffer = undefined;
+		}
+		if (!entry.sampler.type) {
+			entry.sampler = undefined;
+		}
+		if (!entry.texture.sampleType) {
+			entry.texture = undefined;
+		}
+		if (!entry.storageTexture.access) {
+			entry.storageTexture = undefined;
+		}
+		return entry;
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUBufferBindingLayout}
+	 */
+	BufferBindingLayout(start) {
+		return {
+			type: this.enumeration("BufferBindingType", start + 4),
+			hasDynamicOffset: this.mem.loadB32(start + 8),
+			minBindingSize: this.mem.loadU64(start + 16),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUSamplerBindingLayout}
+	 */
+	SamplerBindingLayout(start) {
+		return {
+			type: this.enumeration("SamplerBindingType", start + 4),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUTextureBindingLayout}
+	 */
+	TextureBindingLayout(start) {
+		return {
+			sampleType: this.enumeration("TextureSampleType", start + 4),
+			viewDimension: this.enumeration("TextureViewDimension", start + 8),
+			multisampled: this.mem.loadB32(start + 12),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUStorageTextureBindingLayout}
+	 */
+	StorageTextureBindingLayout(start) {
+		return {
+			access: this.enumeration("StorageTextureAccess", start + 4),
+			format: this.enumeration("TextureFormat", start + 8),
+			viewDimension: this.enumeration("TextureViewDimension", start + 12),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUProgrammableStage}
+	 */
+	ProgrammableStageDescriptor(start) {
+		const constantsArray = this.array(
+			this.mem.loadUint(start + 8 + this.mem.intSize),
+			this.mem.loadPtr(start + 8 + this.mem.intSize*2),
+			this.ConstantEntry,
+			16,
+		);
+		return {
+			module:     this.shaderModules.get(this.mem.loadPtr(start + 4)),
+			entryPoint: this.mem.loadCstring(start + 8),
+			constants:  constantsArray.reduce((prev, curr) => {
+				prev[curr.key] = curr.value;
+				return prev;
+			}, {}),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {{ key: string, value: number }}
+	 */
+	ConstantEntry(start) {
+		return {
+			key:   this.mem.loadCstring(start + 4),
+			value: this.mem.loadF64(start + 8),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUComputePipelineDescriptor}
+	 */
+	ComputePipelineDescriptor(start) {
+		const layoutIdx = this.mem.loadPtr(start + 8)
+		return {
+			label: this.mem.loadCstring(start + 4),
+			layout: layoutIdx > 0 ? this.pipelineLayouts.get(layoutIdx) : undefined,
+			compute: this.ProgrammableStageDescriptor(start + 8 + this.mem.intSize),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUVertexState}
+	 */
+	VertexState(start) {
+		let off = 8 + this.mem.intSize;
+		const constantsArray = this.array(
+			this.mem.loadUint(start + off),
+			this.mem.loadPtr(start + off + this.mem.intSize),
+			this.ConstantEntry,
+			16,
+		);
+
+		off += this.mem.intSize * 2;
+
+		return {
+			module: this.shaderModules.get(this.mem.loadPtr(start + 4)),
+			entryPoint: this.mem.loadCstring(start + 8),
+			constants: constantsArray.reduce((prev, curr) => {
+				prev[curr.key] = curr.value;
+				return prev;
+			}, {}),
+			buffers: this.array(
+				this.mem.loadUint(start + off),
+				this.mem.loadPtr(start + off + this.mem.intSize),
+				this.VertexBufferLayout,
+				this.mem.intSize == 8 ? 32 : 24,
+			),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {?GPUVertexBufferLayout}
+	 */
+	VertexBufferLayout(start) {
+		const stepMode = this.enumeration("VertexStepMode", start + 8);
+		if (stepMode == "vertex-buffer-not-used") {
+			return null;
+		}
+		return {
+			arrayStride: this.mem.loadU64(start + 0),
+			stepMode: stepMode,
+			attributes: this.array(
+				this.mem.loadUint(start + 8 + this.mem.intSize),
+				this.mem.loadPtr(start + 8 + this.mem.intSize*2),
+				this.VertexAttribute,
+				24,
+			),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUVertexAttribute}
+	 */
+	VertexAttribute(start) {
+		return {
+			format:         this.enumeration("VertexFormat", start + 0),
+			offset:         this.mem.loadU64(start + 8),
+			shaderLocation: this.mem.loadU32(start + 16),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUPrimitiveState}
+	 */
+	PrimitiveState(start) {
+		let unclippedDepth = undefined;
+		const nextInChain = this.mem.loadPtr(start);
+		if (nextInChain != 0) {
+			const nextInChainType = this.mem.loadI32(nextInChain + 4);
+    		// PrimitiveDepthClipControl = 0x00000007,
+			if (nextInChainType == 7) {
+				unclippedDepth = this.mem.loadB32(nextInChain + 8);
+			}
+		}
+
+		return {
+			topology:         this.enumeration("PrimitiveTopology", start + 4),
+			stripIndexFormat: this.enumeration("IndexFormat", start + 8),
+			frontFace:        this.enumeration("FrontFace", start + 12),
+			cullMode:         this.enumeration("CullMode", start + 16),
+			unclippedDepth:   unclippedDepth,
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPURenderPipelineDescriptor}
+	 */
+	RenderPipelineDescriptor(start) {
+		const layoutIdx = this.mem.loadPtr(start + 8);
+		const offs = this.mem.intSize == 8 ? [64, 84, 88, 104] : [40, 60, 64, 80];
+		return {
+			label:        this.mem.loadCstring(start + 4),
+			layout:       layoutIdx > 0 ? this.pipelineLayouts.get(layoutIdx) : undefined,
+			vertex:       this.VertexState(start + 8 + this.mem.intSize),
+			primitive:    this.PrimitiveState(start + offs[0]),
+			depthStencil: this.DepthStencilStatePtr(start + offs[1]),
+			multisample:  this.MultisampleState(start + offs[2]),
+			fragment:     this.FragmentStatePtr(start + offs[3]),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUShaderModuleCompilationHint}
+	 */
+	ShaderModuleCompilationHint(start) {
+		return {
+			entryPoint: this.mem.loadCstring(start + 4),
+			layout: this.pipelineLayouts.get(this.mem.loadPtr(start + 8)),
+		};
+	}
+
+	/**
+	 * @param {number} ptr
+	 * @returns {?GPUDepthStencilState}
+	 */
+	DepthStencilStatePtr(ptr) {
+		const start = this.mem.loadPtr(ptr);
+		if (start == 0) {
+			return undefined;
+		}
+
+		return {
+			format:              this.enumeration("TextureFormat", start + 4),
+			depthWriteEnabled:   this.mem.loadB32(start + 8),
+			depthCompare:        this.enumeration("CompareFunction", start + 12),
+			stencilFront:        this.StencilFaceState(start + 16),
+			stencilBack:         this.StencilFaceState(start + 32),
+			stencilReadMask:     this.mem.loadU32(start + 48),
+			stencilWriteMask:    this.mem.loadU32(start + 52),
+			depthBias:           this.mem.loadI32(start + 56),
+			depthBiasSlopeScale: this.mem.loadF32(start + 60),
+			depthBiasClamp:      this.mem.loadF32(start + 64),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUStencilFaceState}
+	 */
+	StencilFaceState(start) {
+		return {
+			compare:     this.enumeration("CompareFunction",  start + 0),
+			failOp:      this.enumeration("StencilOperation", start + 4),
+			depthFailOp: this.enumeration("StencilOperation", start + 8),
+			passOp:      this.enumeration("StencilOperation", start + 12),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUMultisampleState}
+	 */
+	MultisampleState(start) {
+		return {
+			count:                  this.mem.loadU32(start + 4),
+			mask:                   this.mem.loadU32(start + 8),
+			alphaToCoverageEnabled: this.mem.loadB32(start + 12),
+		};
+	}
+
+	/**
+	 * @param {number} ptr
+	 * @returns {?GPUFragmentState}
+	 */
+	FragmentStatePtr(ptr) {
+		const start = this.mem.loadPtr(ptr);
+		if (start == 0) {
+			return undefined;
+		}
+
+		let off = 8 + this.mem.intSize;
+
+		const constantsArray = this.array(
+			this.mem.loadUint(start + off),
+			this.mem.loadPtr(start + off + this.mem.intSize),
+			this.ConstantEntry,
+			16,
+		);
+
+		off += this.mem.intSize * 2;
+
+		return {
+			module:     this.shaderModules.get(this.mem.loadPtr(start + 4)),
+			entryPoint: this.mem.loadCstring(start + 8),
+			constants: constantsArray.reduce((prev, curr) => {
+				prev[curr.key] = curr.value;
+				return prev;
+			}, {}),
+			targets: this.array(
+				this.mem.loadUint(start + off),
+				this.mem.loadPtr(start + off + this.mem.intSize),
+				this.ColorTargetState,
+				16,
+			),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {GPUColorTargetState}
+	 */
+	ColorTargetState(start) {
+		return {
+			format:    this.enumeration("TextureFormat", start + 4),
+			blend:     this.BlendStatePtr(start + 8),
+			writeMask: this.mem.loadU32(start + 12),
+		};
+	}
+
+	/**
+	 * @param {number} ptr
+	 * @returns {?GPUBlendState}
+	 */
+	BlendStatePtr(ptr) {
+		const start = this.mem.loadPtr(ptr);
+		if (start == 0) {
+			return undefined;
+		}
+
+		return {
+			color: this.BlendComponent(start + 0),
+			alpha: this.BlendComponent(start + 12),
+		};
+	}
+
+	/**
+	 * @param {number} start
+	 * @returns {?GPUBlendComponent}
+	 */
+	BlendComponent(start) {
+		return {
+			operation: this.enumeration("BlendOperation", start + 0),
+			srcFactor: this.enumeration("BlendFactor",    start + 4),
+			dstFactor: this.enumeration("BlendFactor",    start + 8),
+		};
+	}
+
+	getInterface() {
+		return {
+			/**
+			 * @param {0|number} descriptorPtr
+			 * @returns {number}
+			 */
+			wgpuCreateInstance: (descriptorPtr) => {
+				if (!navigator.gpu) {
+					console.error("WebGPU is not supported by this browser");
+					return 0;
+				}
+
+				return this.instances.create({});
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} procNamePtr
+			 * @returns {number}
+			 */
+			wgpuGetProcAddress: (deviceIdx, procNamePtr) => {
+				console.error(`unimplemented: wgpuGetProcAddress`);
+				return 0;
+			},
+
+			/* ---------------------- Adapter ---------------------- */
+
+			/**
+			 * @param {number} adapterIdx
+			 * @param {number} featuresPtr
+			 * @returns {number|BigInt}
+			 */
+			wgpuAdapterEnumerateFeatures: (adapterIdx, featuresPtr) => {
+				const adapter = this.adapters.get(adapterIdx);
+				return this.genericEnumerateFeatures(adapter.features, featuresPtr);
+			},
+
+			/**
+			 * @param {number} adapterIdx
+			 * @param {number} supportedLimitsPtr
+			 * @returns {boolean}
+			 */
+			wgpuAdapterGetLimits: (adapterIdx, supportedLimitsPtr) => {
+				const adapter = this.adapters.get(adapterIdx);
+				return this.genericGetLimits(adapter.limits, supportedLimitsPtr);
+			},
+
+			/**
+			 * @param {number} adapterIdx
+			 * @param {number} propertiesPtr
+			 */
+			wgpuAdapterGetProperties: (adapterIdx, propertiesPtr) => {
+				this.assert(propertiesPtr != 0);
+ 				// Unknown adapter.
+				this.mem.storeI32(propertiesPtr + 28, 3);
+				// WebGPU backend.
+				this.mem.storeI32(propertiesPtr + 32, 2);
+			},
+
+			/**
+			 * @param {number} adapterIdx
+			 * @param {number} featureInt
+			 * @returns {boolean}
+			 */
+			wgpuAdapterHasFeature: (adapterIdx, featureInt) => {
+				const adapter = this.adapters.get(adapterIdx);
+				return adapter.features.has(this.enums.FeatureName[featureInt]);
+			},
+
+			/**
+			 * @param {number} adapterIdx
+			 * @param {number} callbackPtr
+			 * @param {0|number} userdata
+			 */
+			wgpuAdapterRequestAdapterInfo: async (adapterIdx, callbackPtr, userdata) => {
+				const adapter  = this.adapters.get(adapterIdx);
+				const callback = this.mem.exports.__indirect_function_table.get(callbackPtr);
+
+				const info = await adapter.requestAdapterInfo();
+
+				const addr = this.mem.exports.wgpu_alloc(16);
+
+				const vendorLength = new TextEncoder().encode(info.vendor).length;
+				const vendorAddr = this.mem.exports.wgpu_alloc(vendorLength);
+				this.mem.storeString(vendorAddr, info.vendor);
+				this.mem.storeI32(addr + 0, vendorAddr);
+
+				const architectureLength = new TextEncoder().encode(info.architecture).length;
+				const architectureAddr = this.mem.exports.wgpu_alloc(architectureLength);
+				this.mem.storeString(architectureAddr, info.architecture);
+				this.mem.storeI32(addr + 4, architectureAddr);
+
+
+				const deviceLength = new TextEncoder().encode(info.device).length;
+				const deviceAddr = this.mem.exports.wgpu_alloc(deviceLength);
+				this.mem.storeString(deviceAddr, info.device);
+				this.mem.storeI32(addr + 8, deviceAddr);
+
+
+				const descriptionLength = new TextEncoder().encode(info.description).length;
+				const descriptionAddr = this.mem.exports.wgpu_alloc(descriptionLength);
+				this.mem.storeString(descriptionAddr, info.description);
+				this.mem.storeI32(addr + 12, descriptionAddr);
+
+				callback(addr, userdata);
+
+				this.mem.exports.wgpu_free(descriptionAddr);
+				this.mem.exports.wgpu_free(deviceAddr);
+				this.mem.exports.wgpu_free(architectureAddr);
+				this.mem.exports.wgpu_free(vendorAddr);
+				this.mem.exports.wgpu_free(addr);
+			},
+
+			/**
+			 * @param {number} adapterIdx
+			 * @param {0|number} descriptorPtr
+			 * @param {number} callbackPtr
+			 * @param {0|number} userdata
+			 */
+			wgpuAdapterRequestDevice: async (adapterIdx, descriptorPtr, callbackPtr, userdata) => {
+				const adapter  = this.adapters.get(adapterIdx);
+				const callback = this.mem.exports.__indirect_function_table.get(callbackPtr);
+
+				/** @type {GPUDeviceDescriptor} */
+				let descriptor;
+				if (descriptorPtr != 0) {
+					descriptor = {
+						label: this.mem.loadCstring(descriptorPtr + 4),
+						requiredFeatures: this.array(
+							this.mem.loadUint(descriptorPtr + 8),
+							this.mem.loadPtr(descriptorPtr  + 8 + this.mem.intSize),
+							this.FeatureNamePtr,
+							4,
+						),
+						requiredLimits: this.RequiredLimitsPtr(descriptorPtr + 8 + this.mem.intSize + 4),
+						defaultQueue:   this.QueueDescriptor(  descriptorPtr + 8 + this.mem.intSize + 4 + 4),
+					};
+				}
+
+				let deviceIdx;
+				try {
+					const device = await adapter.requestDevice(descriptor);
+					deviceIdx = this.devices.create(device);
+					// NOTE: don't callback here, any errors that happen later will then be caught by the catch here.
+				} catch (e) {
+					console.warn(e);
+					callback(1, null, null, userdata);
+				}
+
+				callback(0, deviceIdx, null, userdata);
+			},
+
+			...this.adapters.interface(),
+
+			/* ---------------------- BindGroup ---------------------- */
+
+			...this.bindGroups.interface(true),
+
+			/* ---------------------- BindGroupLayout ---------------------- */
+
+			...this.bindGroupLayouts.interface(true),
+
+			/* ---------------------- Buffer ---------------------- */
+
+			/** @param {number} bufferIdx */
+			wgpuBufferDestroy: (bufferIdx) => {
+				const buffer = this.buffers.get(bufferIdx);
+				buffer.buffer.destroy();
+			},
+
+			/**
+			 * @param {number} bufferIdx
+			 * @param {number|BigInt} offset
+			 * @param {number|BigInt} size
+			 * @returns {number}
+			 */
+			wgpuBufferGetMappedRange: (bufferIdx, offset, size) => {
+				const buffer = this.buffers.get(bufferIdx);
+				offset = this.unwrapBigInt(offset);
+				size   = this.unwrapBigInt(size);
+
+				this.assert(!buffer.mapping, "buffer already mapped");
+
+				const range = buffer.buffer.getMappedRange(offset, size);
+
+				const ptr = this.mem.exports.wgpu_alloc(range.byteLength);
+
+				buffer.mapping = { range: range, ptr: ptr, size: range.byteLength };
+				return ptr;
+			},
+
+			/**
+			 * @param {number} bufferIdx
+			 * @returns {BigInt}
+			 */
+			wgpuBufferGetSize: (bufferIdx) => {
+				const buffer = this.buffers.get(bufferIdx);
+				return BigInt(buffer.buffer.size);
+			},
+
+			/**
+			 * @param {number} bufferIdx
+			 * @returns {number}
+			 */
+			wgpuBufferGetUsage: (bufferIdx) => {
+				const buffer = this.buffers.get(bufferIdx);
+				return buffer.buffer.usage;
+			},
+
+			/**
+			 * @param {number} bufferIdx
+			 * @param {number} mode
+			 * @param {number|BigInt} offset
+			 * @param {number|BigInt} size
+			 * @param {number} callbackPtr
+			 * @param {0|number} userdata
+			 */
+			wgpuBufferMapAsync: async (bufferIdx, mode, offset, size, callbackPtr, userdata) => {
+				const buffer = this.buffers.get(bufferIdx);
+				const callback = this.mem.exports.__indirect_function_table.get(callbackPtr);
+				offset = this.unwrapBigInt(offset);
+				size   = this.unwrapBigInt(size);
+
+				if (buffer.buffer.mapState == "pending") {
+					callback(this.enums.BufferMapAsyncStatus.MappingAlreadyPending, userdata);
+				} else {
+					let result;
+					try {
+						await buffer.buffer.mapAsync(mode, offset, size);
+						result = 0; // Success.
+					} catch(e) {
+						console.warn(e);
+						result = 2; // Unknown error.
+
+						if (e instanceof DomException) {
+							if (e.name == "OperationError") {
+								result = 1; // Validation error.
+							}
+						}
+					}
+
+					callback(result, userdata);
+				}
+			},
+
+			/**
+			 * @param {number} bufferIdx
+			 * @param {number} labelPtr
+			 */
+			wgpuBufferSetLabel: (bufferIdx, labelPtr) => {
+				const buffer = this.buffers.get(bufferIdx);
+				buffer.buffer.label = this.mem.loadCstring(labelPtr);
+			},
+
+			/**
+			 * @param {number} bufferIdx
+			 */
+			wgpuBufferUnmap: (bufferIdx) => {
+				const buffer = this.buffers.get(bufferIdx);
+				this.assert(buffer.mapping, "buffer not mapped");
+
+				const mapping = new Uint8Array(this.mem.memory.buffer, buffer.mapping.ptr, buffer.mapping.size);
+				(new Uint8Array(buffer.mapping.range)).set(mapping);
+
+				buffer.buffer.unmap();
+
+				this.mem.exports.wgpu_free(buffer.mapping.ptr);
+				buffer.mapping = null;
+			},
+
+			...this.buffers.interface(),
+
+			/* ---------------------- CommandBuffer ---------------------- */
+
+			...this.commandBuffers.interface(true),
+
+			/* ---------------------- CommandEncoder ---------------------- */
+
+			/**
+			 * @param {number} commandEncoderIdx
+			 * @param {0|number} descriptorPtr
+			 * @return {number} The compute pass encoder
+			 */
+			wgpuCommandEncoderBeginComputePass: (commandEncoderIdx, descriptorPtr) => {
+				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
+
+				/** @type {?GPUComputePassDescriptor} */
+				let descriptor;
+				if (descriptorPtr != 0) {
+					descriptor = {
+						label:           this.mem.loadCstring(descriptorPtr + 4),
+						timestampWrites: this.ComputePassTimestampWritesPtr(descriptorPtr + 8),
+					};
+				}
+
+				const computePassEncoder = commandEncoder.beginComputePass(descriptor);
+				return this.computePassEncoders.create(computePassEncoder);
+			},
+
+			/**
+			 * @param {number} commandEncoderIdx
+			 * @param {number} descriptorPtr
+			 * @return {number} The render pass encoder
+			 */
+			wgpuCommandEncoderBeginRenderPass: (commandEncoderIdx, descriptorPtr) => {
+				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
+				this.assert(descriptorPtr != 0);
+
+				let maxDrawCount = undefined;
+				const nextInChain = this.mem.loadPtr(descriptorPtr);
+				if (nextInChain != 0) {
+					const nextInChainType = this.mem.loadI32(nextInChain + 4);
+					// RenderPassDescriptorMaxDrawCount = 0x0000000F,
+					if (nextInChainType == 0x0000000F) {
+						maxDrawCount = this.mem.loadU64(nextInChain + 8);
+					}
+				}
+
+				/** @type {GPURenderPassDescriptor} */
+				const descriptor = {
+					label: this.mem.loadCstring(descriptorPtr + 4),
+					colorAttachments: this.array(
+						this.mem.loadUint(descriptorPtr + 8),
+						this.mem.loadPtr(descriptorPtr + 8 + this.mem.intSize),
+						this.RenderPassColorAttachment,
+						56,
+					),
+					depthStencilAttachment: this.RenderPassDepthStencilAttachmentPtr(descriptorPtr + 8 + this.mem.intSize + 4),
+					occlusionQuerySet: this.QuerySet(descriptorPtr + 8 + this.mem.intSize + 4 + 4),
+					timestampWrites: this.RenderPassTimestampWritesPtr(descriptorPtr + 8 + this.mem.intSize + 4 + 4),
+					maxDrawCount: maxDrawCount,
+				};
+
+				const renderPassEncoder = commandEncoder.beginRenderPass(descriptor);
+				return this.renderPassEncoders.create(renderPassEncoder);
+			},
+
+			/**
+			 * @param {number} commandEncoderIdx
+			 * @param {number} bufferIdx
+			 * @param {BigInt} offset
+			 * @param {BigInt} size
+			 */
+			wgpuCommandEncoderClearBuffer: (commandEncoderIdx, bufferIdx, offset, size) => {
+				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
+				const buffer = this.buffers.get(bufferIdx);
+				offset = this.unwrapBigInt(offset);
+				size = this.unwrapBigInt(size);
+				commandEncoder.clearBuffer(buffer.buffer, offset, size);
+			},
+
+			/**
+			 * @param {number} commandEncoderIdx
+			 * @param {number} sourceIdx
+			 * @param {BigInt} sourceOffset
+			 * @param {number} destinationIdx
+			 * @param {BigInt} destinationOffset
+			 * @param {BigInt} size
+			 */
+			wgpuCommandEncoderCopyBufferToBuffer: (commandEncoderIdx, sourceIdx, sourceOffset, destinationIdx, destinationOffset, size) => {
+				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
+				const source = this.buffers.get(sourceIdx);
+				const destination = this.buffers.get(destinationIdx);
+				sourceOffset = this.unwrapBigInt(sourceOffset);
+				destinationOffset = this.unwrapBigInt(destinationOffset);
+				size = this.unwrapBigInt(size);
+				commandEncoder.copyBufferToBuffer(source.buffer, sourceOffset, destination.buffer, destinationOffset, size);
+			},
+
+			/**
+			 * @param {number} commandEncoderIdx
+			 * @param {number} sourcePtr
+			 * @param {number} destinationPtr
+			 * @param {number} copySizePtr
+			 */
+			wgpuCommandEncoderCopyBufferToTexture: (commandEncoderIdx, sourcePtr, destinationPtr, copySizePtr) => {
+				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
+				commandEncoder.copyBufferToTexture(
+					this.ImageCopyBuffer(sourcePtr),
+					this.ImageCopyTexture(destinationPtr),
+					this.Extent3D(copySizePtr),
+				);
+			},
+
+			/**
+			 * @param {number} commandEncoderIdx
+			 * @param {number} sourcePtr
+			 * @param {number} destinationPtr
+			 * @param {number} copySizePtr
+			 */
+			wgpuCommandEncoderCopyTextureToBuffer: (commandEncoderIdx, sourcePtr, destinationPtr, copySizePtr) => {
+				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
+				commandEncoder.copyTextureToBuffer(
+					this.ImageCopyTexture(sourcePtr),
+					this.ImageCopyBuffer(destinationPtr),
+					this.Extent3D(copySizePtr),
+				);
+			},
+
+			/**
+			 * @param {number} commandEncoderIdx
+			 * @param {number} sourcePtr
+			 * @param {number} destinationPtr
+			 * @param {number} copySizePtr
+			 */
+			wgpuCommandEncoderCopyTextureToTexture: (commandEncoderIdx, sourcePtr, destinationPtr, copySizePtr) => {
+				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
+				commandEncoder.copyTextureToTexture(
+					this.ImageCopyTexture(sourcePtr),
+					this.ImageCopyTexture(destinationPtr),
+					this.Extent3D(copySizePtr),
+				);
+			},
+
+			/**
+			 * @param {number} commandEncoderIdx
+			 * @param {0|number} descriptorPtr
+			 * @returns {number} The command buffer.
+			 */
+			wgpuCommandEncoderFinish: (commandEncoderIdx, descriptorPtr) => {
+				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
+
+				/** @type {undefined|GPUCommandBufferDescriptor} */
+				let descriptor;
+				if (descriptorPtr != 0) {
+					descriptor = {
+						label: this.mem.loadCstring(descriptorPtr + 4),
+					};
+				}
+
+				const commandBuffer = commandEncoder.finish(descriptor);
+				return this.commandBuffers.create(commandBuffer);
+			},
+
+			/**
+			 * @param {number} commandEncoderIdx
+			 * @param {number} markerLabelPtr
+			 */
+			wgpuCommandEncoderInsertDebugMarker: (commandEncoderIdx, markerLabelPtr) => {
+				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
+				commandEncoder.insertDebugMarker(this.mem.loadCstring(markerLabelPtr));
+			},
+
+			/**
+			 * @param {number} commandEncoderIdx
+			 */
+			wgpuCommandEncoderPopDebugGroup: (commandEncoderIdx) => {
+				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
+				commandEncoder.popDebugGroup();
+			},
+
+			/**
+			 * @param {number} commandEncoderIdx
+			 * @param {number} markerLabelPtr
+			 */
+			wgpuCommandEncoderPushDebugGroup: (commandEncoderIdx, groupLabelPtr) => {
+				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
+				commandEncoder.pushDebugGroup(this.mem.loadCstring(groupLabelPtr));
+			},
+
+			/**
+			 * @param {number} commandEncoderIdx
+			 * @param {number} querySetIdx
+			 * @param {number} firstQuery
+			 * @param {number} queryCount
+			 * @param {number} destinationIdx
+			 * @param {BigInt} destinationOffset
+			 */
+			wgpuCommandEncoderResolveQuerySet: (commandEncoderIdx, querySetIdx, firstQuery, queryCount, destinationIdx, destinationOffset) => {
+				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
+				const querySet = this.querySets.get(querySetIdx);
+				const destination = this.buffers.get(destinationIdx);
+				destinationOffset = this.unwrapBigInt(destinationOffset);
+				commandEncoder.resolveQuerySet(querySet, firstQuery, queryCount, destination.buffer, destinationOffset);
+			},
+
+			/**
+			 * @param {number} commandEncoderIdx
+			 * @param {number} querySetIdx
+			 * @param {number} queryIndex
+			 */
+			wgpuCommandEncoderWriteTimestamp: (commandEncoderIdx, querySetIdx, queryIndex) => {
+				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
+				const querySet = this.querySets.get(querySetIdx);
+				commandEncoder.writeTimestamp(querySet, queryIndex);
+			},
+
+			...this.commandEncoders.interface(true),
+
+			/* ---------------------- ComputePassEncoder ---------------------- */
+
+
+			/**
+			 * @param {number} computePassEncoderIdx
+			 * @param {number} workgroupCountX
+			 * @param {number} workgroupCountY
+			 * @param {number} workgroupCountZ
+			 */
+			wgpuComputePassEncoderDispachWorkgroups: (computePassEncoderIdx, workgroupCountX, workgroupCountY, workgroupCountZ) => {
+				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
+				computePassEncoder.dispatchWorkgroups(workgroupCountX, workgroupCountY, workgroupCountZ);
+			},
+
+			/**
+			 * @param {number} computePassEncoderIdx
+			 * @param {number} indirectBufferIdx
+			 * @param {BigInt} indirectOffset
+			 */
+			wgpuComputePassEncoderDispachWorkgroupsIndirect: (computePassEncoderIdx, indirectBufferIdx, indirectOffset) => {
+				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
+				const indirectBuffer = this.buffers.get(indirectBufferIdx);
+				indirectOffset = this.unwrapBigInt(indirectOffset);
+				computePassEncoder.dispatchWorkgroupsIndirect(indirectBuffer.buffer, indirectOffset);
+			},
+
+			/**
+			 * @param {number} computePassEncoderIdx
+			 */
+			wgpuComputePassEncoderEnd: (computePassEncoderIdx) => {
+				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
+				computePassEncoder.end();
+			},
+
+			/**
+			 * @param {number} computePassEncoderIdx
+			 * @param {number} markerLabelPtr
+			 */
+			wgpuComputePassEncoderInsertDebugMarker: (computePassEncoderIdx, markerLabelPtr) => {
+				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
+				computePassEncoder.insertDebugMarker(this.mem.loadCstring(markerLabelPtr));
+			},
+
+			/**
+			 * @param {number} computePassEncoderIdx
+			 */
+			wgpuComputePassEncoderPopDebugGroup: (computePassEncoderIdx) => {
+				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
+				computePassEncoder.popDebugGroup();
+			},
+
+			/**
+			 * @param {number} computePassEncoderIdx
+			 * @param {number} markerLabelPtr
+			 */
+			wgpuComputePassEncoderPushDebugGroup: (computePassEncoderIdx, groupLabelPtr) => {
+				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
+				computePassEncoder.pushDebugGroup(this.mem.loadCstring(groupLabelPtr));
+			},
+
+			/**
+			 * @param {number} computePassEncoderIdx
+			 * @param {number} groupIndex
+			 * @param {0|number} groupIdx
+			 * @param {number|BigInt} dynamicOffsetCount
+			 * @param {number} dynamicOffsetsPtr
+			 */
+			wgpuComputePassEncoderSetBindGroup: (computePassEncoderIdx, groupIndex, groupIdx, dynamicOffsetCount, dynamicOffsetsPtr) => {
+				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
+				dynamicOffsetCount = this.unwrapBigInt(dynamicOffsetCount);
+
+				let bindGroup;
+				if (groupIdx != 0) {
+					bindGroup = this.bindGroups.get(groupIdx);
+				}
+
+				const dynamicOffsets = [];
+				for (let i = 0; i < dynamicOffsetCount; i += 1) {
+					dynamicOffsets.push(this.mem.loadU32(dynamicOffsetsPtr));
+					dynamicOffsetsPtr += 4;
+				}
+
+				computePassEncoder.setBindGroup(groupIndex, bindGroup, dynamicOffsets);
+			},
+
+			/**
+			 * @param {number} computePassEncoderIdx
+			 * @param {number} pipelineIdx
+			 */
+			wgpuComputePassEncoderSetPipeline: (computePassEncoderIdx, pipelineIdx) => {
+				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
+				const pipeline = this.computePipelines.get(pipelineIdx);
+				computePassEncoder.setPipeline(pipeline);
+			},
+
+			...this.computePassEncoders.interface(true),
+
+			/* ---------------------- ComputePipeline ---------------------- */
+
+			/**
+			 * @param {number} computePipelineIdx
+			 * @param {number} groupIndex
+			 * @returns {number}
+			 */
+			wgpuComputePipelineGetBindGroupLayout: (computePipelineIdx, groupIndex) => {
+				const computePipeline = this.computePipelines.get(computePipelineIdx);
+				const bindGroupLayout = computePipeline.getBindGroupLayout(groupIndex);
+				return this.bindGroupLayouts.create(bindGroupLayout);
+			},
+
+			...this.computePipelines.interface(true),
+
+			/* ---------------------- Device ---------------------- */
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} descriptorPtr
+			 * @returns {number} The bind group.
+			 */
+			wgpuDeviceCreateBindGroup: (deviceIdx, descriptorPtr) => {
+				const device = this.devices.get(deviceIdx);
+				this.assert(descriptorPtr != 0);
+
+				/** @type {GPUBindGroupDescriptor} */
+				const descriptor = {
+					label: this.mem.loadCstring(descriptorPtr + 4),
+					layout: this.bindGroupLayouts.get(this.mem.loadPtr(descriptorPtr + 8)),
+					entries: this.array(
+						this.mem.loadUint(descriptorPtr + 8 + this.mem.intSize),
+						this.mem.loadPtr(descriptorPtr + 8 + this.mem.intSize * 2),
+						this.BindGroupEntry,
+						40,
+					),
+				};
+			
+				const bindGroup = device.createBindGroup(descriptor);
+				return this.bindGroups.create(bindGroup);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} descriptorPtr
+			 * @returns {number} The bind group layout.
+			 */
+			wgpuDeviceCreateBindGroupLayout: (deviceIdx, descriptorPtr) => {
+				const device = this.devices.get(deviceIdx);
+				this.assert(descriptorPtr != 0);
+
+				/** @type {GPUBindGroupLayoutDescriptor} */
+				const descriptor = {
+					label: this.mem.loadCstring(descriptorPtr + 4),
+					entries: this.array(
+						this.mem.loadUint(descriptorPtr + 8),
+						this.mem.loadPtr(descriptorPtr + 8 + this.mem.intSize),
+						this.BindGroupLayoutEntry,
+						80,
+					),
+				};
+
+				const bindGroupLayout = device.createBindGroupLayout(descriptor);
+				return this.bindGroupLayouts.create(bindGroupLayout);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} descriptorPtr
+			 * @returns {number} The buffer.
+			 */
+			wgpuDeviceCreateBuffer: (deviceIdx, descriptorPtr) => {
+				const device = this.devices.get(deviceIdx);
+				this.assert(descriptorPtr != 0);
+
+				/** @type {GPUBufferDescriptor} */
+				const descriptor = {
+					label: this.mem.loadCstring(descriptorPtr + 4),
+					usage: this.mem.loadU32(descriptorPtr + 8),
+					size: this.mem.loadU64(descriptorPtr + 16),
+					mappedAtCreation: this.mem.loadB32(descriptorPtr + 24),
+				};
+
+				const buffer = device.createBuffer(descriptor);
+				return this.buffers.create({buffer: buffer, mapping: null});
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {0|number} descriptorPtr
+			 * @returns {number} The command encoder.
+			 */
+			wgpuDeviceCreateCommandEncoder: (deviceIdx, descriptorPtr) => {
+				const device = this.devices.get(deviceIdx);
+
+				/** @type {GPUCommandEncoderDescriptor} */
+				let descriptor;
+				if (descriptor != 0) {
+					descriptor = {
+						label: this.mem.loadCstring(descriptorPtr + 4),
+					};
+				}
+
+				const commandEncoder = device.createCommandEncoder(descriptor);
+				return this.commandEncoders.create(commandEncoder);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} descriptorPtr
+			 * @returns {number} The compute pipeline.
+			 */
+			wgpuDeviceCreateComputePipeline: (deviceIdx, descriptorPtr) => {
+				const device = this.devices.get(deviceIdx);
+				this.assert(descriptorPtr != 0);
+				const computePipeline = device.createComputePipeline(this.ComputePipelineDescriptor(descriptorPtr));
+				return this.computePipelines.create(computePipeline);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} descriptorPtr
+			 * @param {number} callbackPtr
+			 * @param {number} userdata
+			 */
+			wgpuDeviceCreateComputePipelineAsync: async (deviceIdx, descriptorPtr, callbackPtr, userdata) => {
+				const device = this.devices.get(deviceIdx);
+				const callback = this.mem.exports.__indirect_function_table.get(callbackPtr);
+				this.assert(descriptorPtr != 0);
+
+				let result;
+				let resultIdx;
+				try {
+					const computePipeline = await device.createComputePipelineAsync(this.ComputePipelineDescriptor(descriptorPtr));
+					resultIdx = this.computePipelines.create(computePipeline);
+					result = 0; /* Success */
+					// NOTE: don't callback here, any errors that happen later will then be caught by the catch here.
+				} catch (e) {
+					console.warn(e);
+					result = 5; /* Unknown error */
+				}
+
+				callback(result, resultIdx, null, userdata);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} descriptorPtr
+			 * @returns {number} The pipeline layout.
+			 */
+			wgpuDeviceCreatePipelineLayout: (deviceIdx, descriptorPtr) => {
+				const device = this.devices.get(deviceIdx);
+				this.assert(descriptorPtr != 0);
+
+				/** @type {GPUPipelineLayoutDescriptor} */
+				const descriptor = {
+					label: this.mem.loadCstring(descriptorPtr + 4),
+					bindGroupLayouts: this.array(
+						this.mem.loadUint(descriptorPtr + 8),
+						this.mem.loadPtr(descriptorPtr + 8 + this.mem.intSize),
+						(ptr) => this.bindGroupLayouts.get(this.mem.loadPtr(ptr)),
+						4,
+					),
+				};
+
+				const pipelineLayout = device.createPipelineLayout(descriptor);
+				return this.pipelineLayouts.create(pipelineLayout);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} descriptorPtr
+			 * @returns {number} The query set.
+			 */
+			wgpuDeviceCreateQuerySet: (deviceIdx, descriptorPtr) => {
+				const device = this.devices.get(deviceIdx);
+				this.assert(descriptorPtr != 0);
+
+				/** @type {GPUQuerySetDescriptor} */
+				const descriptor = {
+					label: this.mem.loadCstring(descriptorPtr + 4),
+					type:  this.QueryType(descriptorPtr + 8),
+					count: this.mem.loadU32(descriptorPtr + 12),
+				};
+
+				const querySet = device.createQuerySet(descriptor);
+				return this.querySets.create(querySet);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} descriptorPtr
+			 * @returns {number} The query set.
+			 */
+			wgpuDeviceCreateRenderBundleEncoder: (deviceIdx, descriptorPtr) => {
+				const device = this.devices.get(deviceIdx);
+				this.assert(descriptorPtr != 0);
+
+				/** @type {GPURenderBundleEncoderDescriptor} */
+				const descriptor = {
+					label: this.mem.loadCstring(descriptorPtr + 4),
+					colorFormats: this.array(
+						this.mem.loadUint(descriptorPtr + 8),
+						this.mem.loadPtr(descriptorPtr + 8 + this.mem.intSize),
+						this.TextureFormat,
+						4,
+					),
+					depthStencilFormat: this.enumeration("TextureFormat", descriptorPtr + 8 + this.mem.intSize + 4),
+					sampleCount:        this.mem.loadU32(descriptorPtr + 8 + this.mem.intSize + 8),
+					depthReadOnly:      this.mem.loadB32(descriptorPtr + 8 + this.mem.intSize + 12),
+					stencilReadOnly:    this.mem.loadB32(descriptorPtr + 8 + this.mem.intSize + 16),
+				};
+
+				const renderBundleEncoder = device.createRenderBundleEncoder(descriptor);
+				return this.renderBundleEncoders.create(renderBundleEncoder);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} descriptorPtr
+			 * @returns {number} The render pipeline.
+			 */
+			wgpuDeviceCreateRenderPipeline: (deviceIdx, descriptorPtr) => {
+				const device = this.devices.get(deviceIdx);
+				this.assert(descriptorPtr != 0);
+
+				const descriptor = this.RenderPipelineDescriptor(descriptorPtr);
+				const renderPipeline = device.createRenderPipeline(descriptor);
+				return this.renderPipelines.create(renderPipeline);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} descriptorPtr
+			 * @param {number} callbackPtr
+			 * @param {number} userdata
+			 */
+			wgpuDeviceCreateRenderPipelineAsync: async (deviceIdx, descriptorPtr, callbackPtr, userdata) => {
+				const device = this.devices.get(deviceIdx);
+				const callback = this.mem.exports.__indirect_function_table.get(callbackPtr);
+				this.assert(descriptorPtr != 0);
+
+				let result;
+				let resultIdx;
+				try {
+					const renderPipeline = await device.createRenderPipelineAsync(this.RenderPipelineDescriptor(descriptorPtr));
+					resultIdx = this.renderPipelines.create(renderPipeline);
+					result = 0; /* Success */
+					// NOTE: don't callback here, any errors that happen later will then be caught by the catch here.
+				} catch (e) {
+					console.warn(e);
+					result = 5; /* Unknown error */
+				}
+
+				callback(result, resultIdx, null, userdata);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {0|number} descriptorPtr
+			 * @returns {number} The sampler.
+			 */
+			wgpuDeviceCreateSampler: (deviceIdx, descriptorPtr) => {
+				const device = this.devices.get(deviceIdx);
+
+				/** @type {?GPUSamplerDescriptor} */
+				let descriptor;
+				if (descriptorPtr != 0) {
+					descriptor = {
+						label:         this.mem.loadCstring(descriptorPtr + 4),
+						addressModeU:  this.enumeration("AddressMode", descriptorPtr + 8),
+						addressModeV:  this.enumeration("AddressMode", descriptorPtr + 12),
+						addressModeW:  this.enumeration("AddressMode", descriptorPtr + 16),
+						magFilter:     this.enumeration("FilterMode", descriptorPtr + 20),
+						minFilter:     this.enumeration("FilterMode", descriptorPtr + 24),
+						mipMapFilter:  this.enumeration("MipmapFilterMode", descriptorPtr + 28),
+						lodMinClamp:   this.mem.loadF32(descriptorPtr + 32),
+						lodMaxClamp:   this.mem.loadF32(descriptorPtr + 36),
+						compare:       this.enumeration("CompareFunction", descriptorPtr + 40),
+						maxAnisotropy: this.mem.loadU16(descriptorPtr + 44),
+					};
+				}
+
+				const sampler = device.createSampler(descriptor);
+				return this.samplers.create(sampler);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} descriptorPtr
+			 * @returns {number} The shader module.
+			 */
+			wgpuDeviceCreateShaderModule: (deviceIdx, descriptorPtr) => {
+				const device = this.devices.get(deviceIdx);
+				this.assert(descriptorPtr != 0);
+
+				const nextInChain = this.mem.loadPtr(descriptorPtr);
+				const nextInChainType = this.mem.loadI32(nextInChain + 4);
+
+				// ShaderModuleWGSLDescriptor = 0x00000006,
+				if (nextInChainType != 6) {
+					throw new TypeError(`Descriptor type should be 'ShaderModuleWGSLDescriptor', got ${nextInChainType}`);
+				}
+
+				/** @type {GPUShaderModuleDescriptor} */
+				const descriptor = {
+					label: this.mem.loadCstring(descriptorPtr + 4),
+					code: this.mem.loadCstring(nextInChain + 8),
+					compilationHints: this.array(
+						this.mem.loadUint(descriptorPtr + 8),
+						this.mem.loadPtr(descriptorPtr + 8 + this.mem.intSize),
+						this.ShaderModuleCompilationHint,
+						12,
+					),
+				};
+
+				const shaderModule = device.createShaderModule(descriptor);
+				return this.shaderModules.create(shaderModule);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} descriptorPtr
+			 * @returns {number} The texture.
+			 */
+			wgpuDeviceCreateTexture: (deviceIdx, descriptorPtr) => {
+				const device = this.devices.get(deviceIdx);
+				this.assert(descriptorPtr != 0);
+
+				/** @type {GPUTextureDescriptor} */
+				const descriptor = {
+					label: this.mem.loadCstring(descriptorPtr + 4),
+					usage: this.mem.loadU32(descriptorPtr + 8),
+					dimension: this.enumeration("TextureDimension", descriptorPtr + 12),
+					size: this.Extent3D(descriptorPtr + 16),
+					format: this.enumeration("TextureFormat", descriptorPtr + 28),
+					mipLevelCount: this.mem.loadU32(descriptorPtr + 32),
+					sampleCount: this.mem.loadU32(descriptorPtr + 36),
+					viewFormats: this.array(
+						this.mem.loadUint(descriptorPtr + 40),
+						this.mem.loadPtr(descriptorPtr + 40 + this.mem.intSize),
+						(ptr) => this.enumeration("TextureFormat", ptr),
+						4,
+					),
+				};
+
+				const texture = device.createTexture(descriptor);
+				return this.textures.create(texture);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 */
+			wgpuDeviceDestroy: (deviceIdx) => {
+				const device = this.devices.get(deviceIdx);
+				device.destroy();
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} featuresPtr
+			 * @returns {number|BigInt}
+			 */
+			wgpuDeviceEnumerateFeatures: (deviceIdx, featuresPtr) => {
+				const device = this.devices.get(deviceIdx);
+				return this.genericEnumerateFeatures(device.features, featuresPtr);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} limitsPtr
+			 * @returns {boolean}
+			 */
+			wgpuDeviceGetLimits: (deviceIdx, limitsPtr) => {
+				const device = this.devices.get(deviceIdx);
+				return this.genericGetLimits(device.limits, limitsPtr);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @returns {number}
+			 */
+			wgpuDeviceGetQueue: (deviceIdx) => {
+				const device = this.devices.get(deviceIdx);
+				return this.queues.create(device.queue);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} featureInt
+			 * @returns {boolean}
+			 */
+			wgpuDeviceHasFeature: (deviceIdx, featureInt) => {
+				const device = this.devices.get(deviceIdx);
+				return device.features.has(this.enums.FeatureName[featureInt]);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} callbackPtr
+			 * @param {number} userdata
+			 */
+			wgpuDevicePopErrorScope: async (deviceIdx, callbackPtr, userdata) => {
+				const device = this.devices.get(deviceIdx);
+				const callback = this.mem.exports.__indirect_function_table.get(callbackPtr);
+				const error = await device.popErrorScope();
+				if (!error) {
+					callback(0, null, userdata);
+					return;
+				}
+				console.warn(error);
+				let status = 4;
+				if (error instanceof GPUValidationError) {
+					status = 1;
+				} else if (error instanceof GPUOutOfMemoryError) {
+					status = 2;
+				} else if (error instanceof GPUInternalError) {
+					status = 3;
+				}
+				callback(status, null, userdata);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} filterInt 
+			 */
+			wgpuDevicePushErrorScope: (deviceIdx, filterInt) => {
+				const device = this.devices.get(deviceIdx);
+				device.pushErrorScope(this.enums.ErrorFilter[filterInt]);
+			},
+
+			/**
+			 * @param {number} deviceIdx
+			 * @param {number} callbackPtr
+			 * @param {number} userdata
+			 */
+			wgpuDeviceSetUncapturedErrorCallback: (deviceIdx, callbackPtr, userdata) => {
+				const device = this.devices.get(deviceIdx);
+				const callback = this.mem.exports.__indirect_function_table.get(callbackPtr);
+
+				device.onuncapturederror = (ev) => {
+					console.warn(ev.error);
+					let status = 4;
+					if (error instanceof GPUValidationError) {
+						status = 1;
+					} else if (error instanceof GPUOutOfMemoryError) {
+						status = 2;
+					} else if (error instanceof GPUInternalError) {
+						status = 3;
+					}
+					callback(status, null, userdata);
+				};
+			},
+
+			...this.devices.interface(true),
+
+			/* ---------------------- Instance ---------------------- */
+
+			/**
+			 * @param {number} instanceIdx
+			 * @param {number} descriptorPtr
+			 */
+			wgpuInstanceCreateSurface: (instanceIdx, descriptorPtr) => {
+				this.assert(instanceIdx > 0);
+				this.assert(descriptorPtr != 0);
+
+				const nextInChain = this.mem.loadPtr(descriptorPtr);
+				const nextInChainType = this.mem.loadI32(nextInChain + 4);
+
+    			// SurfaceDescriptorFromCanvasHTMLSelector = 0x00000004,
+				if (nextInChainType != 4) {
+					throw new TypeError(`Descriptor type should be 'SurfaceDescriptorFromCanvasHTMLSelector', got ${nextInChainType}`);
+				}
+
+				const selector = this.mem.loadCstring(nextInChain + 8);
+				const surface = document.querySelector(selector);
+				if (!surface) {
+					throw new Error(`Selector '${selector}' did not match any element`);
+				}
+				if (!(surface instanceof HTMLCanvasElement)) {
+					throw new Error('Selector matches an element that is not a canvas');
+				}
+
+				return this.surfaces.create(surface);
+			},
+
+			/**
+			 * @param {number} instanceIdx
+			 * @param {number} featureInt
+			 * @returns {boolean}
+			 */
+			wgpuInstanceHasWGSLLanguageFeature: (instanceIdx, featureInt) => {
+				return navigator.gpu.wgslLanguageFeatures.has(this.enums.WGSLFeatureName[featureInt]);
+			},
+
+			/**
+			 * @param {number} instanceIdx
+			 */
+			wgpuInstanceProcessEvents: (instanceIdx) => {
+				console.warn("unimplemented: wgpuInstanceProcessEvents");
+			},
+
+			/**
+			 * @param {number} instanceIdx
+			 * @param {0|number} optionsPtr
+			 * @param {number} callbackPtr
+			 * @param {number} userdata
+			 */
+			wgpuInstanceRequestAdapter: async (instanceIdx, optionsPtr, callbackPtr, userdata) => {
+				this.assert(instanceIdx > 0);
+				const callback = this.mem.exports.__indirect_function_table.get(callbackPtr);
+
+				/** @type {GPURequestAdapterOptions} */
+				let options;
+				if (optionsPtr != 0) {
+					options = {
+						powerPreference: this.enumeration("PowerPreference", optionsPtr + 8),
+						forceFallbackAdapter: this.mem.loadB32(optionsPtr + 16),
+					};
+				}
+
+				let adapterIdx;
+				try {
+					const adapter = await navigator.gpu.requestAdapter(options);
+					adapterIdx = this.adapters.create(adapter);
+					// NOTE: don't callback here, any errors that happen later will then be caught by the catch here.
+				} catch(e) {
+					console.warn(e);
+					callback(2, null, null, userdata);
+				}
+
+				callback(0, adapterIdx, null, userdata);
+			},
+
+			...this.instances.interface(false),
+
+			/* ---------------------- PipelineLayout ---------------------- */
+
+			...this.pipelineLayouts.interface(true),
+
+			/* ---------------------- QuerySet ---------------------- */
+
+			/**
+			 * @param {number} querySetIdx
+			 */
+			wgpuQuerySetDestroy: (querySetIdx) => {
+				const querySet = this.querySets.get(querySetIdx);
+				querySet.destroy();
+			},
+
+			/**
+			 * @param {number} querySetIdx
+			 * @returns {number}
+			 */
+			wgpuQuerySetGetCount: (querySetIdx) => {
+				const querySet = this.querySets.get(querySetIdx);
+				return querySet.count;
+			},
+
+			/**
+			 * @param {number} querySetIdx
+			 * @returns {number}
+			 */
+			wgpuQuerySetGetType: (querySetIdx) => {
+				const querySet = this.querySets.get(querySetIdx);
+				return this.enums.QueryType.indexOf(querySet.type);
+			},
+
+			...this.querySets.interface(true),
+
+			/* ---------------------- Queue ---------------------- */
+
+			/**
+			 * @param {number} queueIdx
+			 * @param {number} callbackPtr
+			 * @param {number} userdata
+			 */
+			wgpuQueueOnSubmittedWorkDone: async (queueIdx, callbackPtr, userdata) => {
+				const queue = this.queues.get(queueIdx);
+				const callback = this.mem.exports.__indirect_function_table.get(callbackPtr);
+				let result;
+				try {
+					await queue.onSubmittedWorkDone();
+					result = 0;
+				} catch(e) {
+					console.warn(e);
+					result = 1;
+				}
+				callback(result, userdata);
+			},
+
+			/**
+			 * @param {number} queueIdx
+			 * @param {BigInt|number} commandCount
+			 * @param {number} commandsPtr
+			 */
+			wgpuQueueSubmit: (queueIdx, commandCount, commandsPtr) => {
+				const queue = this.queues.get(queueIdx);
+				const commands = this.array(
+					this.unwrapBigInt(commandCount),
+					commandsPtr,
+					(ptr) => this.commandBuffers.get(this.mem.loadPtr(ptr)),
+					4,
+				);
+				queue.submit(commands);
+			},
+
+			/**
+			 * @param {number} queueIdx
+			 * @param {number} bufferIdx 
+			 * @param {BigInt} bufferOffset
+			 * @param {number} dataPtr
+			 * @param {number|BigInt} size
+			 */
+			wgpuQueueWriteBuffer: (queueIdx, bufferIdx, bufferOffset, dataPtr, size) => {
+				const queue = this.queues.get(queueIdx);
+				const buffer = this.buffers.get(bufferIdx);
+				bufferOffset = this.unwrapBigInt(bufferOffset);
+				size = this.unwrapBigInt(size);
+				queue.writeBuffer(buffer.buffer, bufferOffset, this.mem.loadBytes(dataPtr, size), 0, size);
+			},
+
+			/**
+			 * @param {number} queueIdx
+			 * @param {number} destinationPtr
+			 * @param {number} dataPtr
+			 * @param {number|BigInt} dataSize
+			 * @param {number} dataLayoutPtr
+			 * @param {number} writeSizePtr
+			 */
+			wgpuQueueWriteTexture: (queueIdx, destinationPtr, dataPtr, dataSize, dataLayoutPtr, writeSizePtr) => {
+				const queue = this.queues.get(queueIdx);
+				const destination = this.ImageCopyTexture(destinationPtr);
+				dataSize = this.unwrapBigInt(dataSize);
+				const dataLayout = this.TextureDataLayout(dataLayoutPtr);
+				const writeSize = this.Extent3D(writeSizePtr);
+				queue.writeTexture(destination, this.mem.loadBytes(dataPtr, dataSize), dataLayout, writeSize);
+			},
+
+			...this.queues.interface(true),
+
+			/* ---------------------- RenderBundle ---------------------- */
+
+			...this.renderBundles.interface(true),
+
+			/* ---------------------- RenderBundleEncoder ---------------------- */
+
+			/**
+			 * @param {number} renderBundleEncoderIdx
+			 * @param {number} vertexCount
+			 * @param {number} instanceCount
+			 * @param {number} firstVertex
+			 * @param {number} firstInstance
+			 */
+			wgpuRenderBundleEncoderDraw: (renderBundleEncoderIdx, vertexCount, instanceCount, firstVertex, firstInstance) => {
+				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
+				renderBundleEncoder.draw(vertexCount, instanceCount, firstVertex, firstInstance);
+			},
+
+			/**
+			 * @param {number} renderBundleEncoderIdx
+			 * @param {number} indexCount
+			 * @param {number} instanceCount
+			 * @param {number} firstIndex
+			 * @param {number} baseVertex
+			 * @param {number} firstInstance
+			 */
+			wgpuRenderBundleEncoderDrawIndexed: (renderBundleEncoderIdx, indexCount, instanceCount, firstIndex, baseVertex, firstInstance) => {
+				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
+				renderBundleEncoder.drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance);
+			},
+
+			/**
+			 * @param {number} renderBundleEncoderIdx
+			 * @param {number} indirectBufferIdx
+			 * @param {BigInt} indirectOffset
+			 */
+			wgpuRenderBundleEncoderDrawIndexedIndirect: (renderBundleEncoderIdx, indirectBufferIdx, indirectOffset) => {
+				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
+				indirectOffset = this.unwrapBigInt(indirectOffset);
+				const buffer = this.buffers.get(indirectBufferIdx);
+				renderBundleEncoder.drawIndexedIndirect(buffer.buffer, indirectOffset);
+			},
+
+			/**
+			 * @param {number} renderBundleEncoderIdx
+			 * @param {number} indirectBufferIdx
+			 * @param {BigInt} indirectOffset
+			 */
+			wgpuRenderBundleEncoderDrawIndirect: (renderBundleEncoderIdx, indirectBufferIdx, indirectOffset) => {
+				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
+				indirectOffset = this.unwrapBigInt(indirectOffset);
+				const buffer = this.buffers.get(indirectBufferIdx);
+				renderBundleEncoder.drawIndirect(buffer.buffer, indirectOffset);
+			},
+
+			/**
+			 * @param {number} renderBundleEncoderIdx
+			 * @param {0|number} descriptorPtr
+			 * @returns {number}
+			 */
+			wgpuRenderBundleEncoderFinish: (renderBundleEncoderIdx, descriptorPtr) => {
+				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
+
+				/** @type {?GPURenderBundleDescriptor} */
+				let descriptor;
+				if (descriptorPtr != 0) {
+					descriptor = {
+						label: this.mem.loadCstring(descriptorPtr + 4),
+					};
+				}
+
+				const renderBundle = renderBundleEncoder.finish(descriptor);
+				return this.renderBundles.create(renderBundle);
+			},
+
+			/**
+			 * @param {number} renderBundleEncoderIdx
+			 * @param {number} markerLabelPtr
+			 */
+			wgpuRenderBundleEncoderInsertDebugMarker: (renderBundleEncoderIdx, markerLabelPtr) => {
+				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
+				this.assert(markerLabelPtr != 0);
+				const markerLabel = this.mem.loadCstring(markerLabelPtr);
+				renderBundleEncoder.insertDebugMarker(markerLabel);
+			},
+
+			/**
+			 * @param {number} renderBundleEncoderIdx
+			 */
+			wgpuRenderBundleEncoderPopDebugGroup: (renderBundleEncoderIdx) => {
+				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
+				renderBundleEncoder.popDebugGroup();
+			},
+
+			/**
+			 * @param {number} renderBundleEncoderIdx
+			 * @param {number} groupLabelPtr
+			 */
+			wgpuRenderBundleEncoderPushDebugGroup: (renderBundleEncoderIdx, groupLabelPtr) => {
+				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
+				this.assert(groupLabelPtr!= 0);
+				const groupLabel = this.mem.loadCstring(groupLabelPtr);
+				renderBundleEncoder.pushDebugGroup(groupLabel);
+			},
+
+			/**
+			 * @param {number} renderBundleEncoderIdx
+			 * @param {number} groupIndex
+			 * @param {0|number} groupIdx
+			 * @param {number|BigInt} dynamicOffsetCount
+			 * @param {number} dynamicOffsetsPtr
+			 */
+			wgpuRenderBundleEncoderSetBindGroup: (renderBundleEncoderIdx, groupIndex, groupIdx, dynamicOffsetCount, dynamicOffsetsPtr) => {
+				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
+
+				let group;
+				if (groupIdx > 0) {
+					group = this.bindGroups.get(groupIdx);
+				}
+
+				dynamicOffsetCount = this.unwrapBigInt(dynamicOffsetCount);
+				const dynamicOffsets = this.array(dynamicOffsetCount, dynamicOffsetsPtr, this.mem.loadU32, 4);	
+
+				renderBundleEncoder.setBindGroup(groupIndex, group, dynamicOffsets);
+			},
+
+			/**
+			 * @param {number} renderBundleEncoderIdx
+			 * @param {number} bufferIdx
+			 * @param {number} formatInt
+			 * @param {BigInt} offset
+			 * @param {BigInt} size
+			 */
+			wgpuRenderBundleEncoderSetIndexBuffer: (renderBundleEncoderIdx, bufferIdx, formatInt, offset, size) => {
+				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
+				const buffer = this.buffers.get(bufferIdx);
+				const format = this.enums.IndexFormat[formatInt];
+				offset = this.unwrapBigInt(offset);
+				size = this.unwrapBigInt(size);
+				renderBundleEncoder.setIndexBuffer(buffer.buffer, format, offset, size);
+			},
+
+			/**
+			 * @param {number} renderBundleEncoderIdx
+			 * @param {number} pipelineIdx
+			 */
+			wgpuRenderBundleEncoderSetPipeline: (renderBundleEncoderIdx, pipelineIdx) => {
+				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
+				const pipeline = this.renderPipelines.get(pipelineIdx);
+				renderBundleEncoder.setPipeline(pipeline);
+			},
+
+			/**
+			 * @param {number} renderBundleEncoderIdx
+			 * @param {number} slot
+			 * @param {0|number} bufferIdx
+			 * @param {BigInt} offset
+			 * @param {BigInt} size
+			 */
+			wgpuRenderBundleEncoderSetVertexBuffer: (renderBundleEncoderIdx, slot, bufferIdx, offset, size) => {
+				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
+
+				let buffer;
+				if (bufferIdx > 0) {
+					buffer = this.buffers.get(bufferIdx).buffer;
+				}
+
+				offset = this.unwrapBigInt(offset);
+				size = this.unwrapBigInt(size);
+				renderBundleEncoder.setVertexBuffer(slot, buffer, offset, size);
+			},
+
+			...this.renderBundleEncoders.interface(true),
+
+			/* ---------------------- RenderPassEncoder ---------------------- */
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} queryIndex
+			 */
+			wgpuRenderPassEncoderBeginOcclusionQuery: (renderPassEncoderIdx, queryIndex) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				renderPassEncoder.beginOcclusionQuery(queryIndex);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} vertexCount
+			 * @param {number} instanceCount
+			 * @param {number} firstVertex
+			 * @param {number} firstInstance
+			 */
+			wgpuRenderPassEncoderDraw: (renderPassEncoderIdx, vertexCount, instanceCount, firstVertex, firstInstance) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				renderPassEncoder.draw(vertexCount, instanceCount, firstVertex, firstInstance);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} indexCount
+			 * @param {number} instanceCount
+			 * @param {number} firstIndex
+			 * @param {number} baseVertex
+			 * @param {number} firstInstance
+			 */
+			wgpuRenderPassEncoderDrawIndexed: (renderPassEncoderIdx, indexCount, instanceCount, firstIndex, baseVertex, firstInstance) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				renderPassEncoder.drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} indirectBufferIdx
+			 * @param {BigInt} indirectOffset
+			 */
+			wgpuRenderPassEncoderDrawIndexedIndirect: (renderPassEncoderIdx, indirectBufferIdx, indirectOffset) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				const buffer = this.buffers.get(indirectBufferIdx);
+				indirectOffset = this.unwrapBigInt(indirectOffset);
+				renderPassEncoder.drawIndexedIndirect(buffer.buffer, indirectOffset);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} indirectBufferIdx
+			 * @param {BigInt} indirectOffset
+			 */
+			wgpuRenderPassEncoderDrawIndirect: (renderPassEncoderIdx, indirectBufferIdx, indirectOffset) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				const buffer = this.buffers.get(indirectBufferIdx);
+				indirectOffset = this.unwrapBigInt(indirectOffset);
+				renderPassEncoder.drawIndirect(buffer.buffer, indirectOffset);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 */
+			wgpuRenderPassEncoderEnd: (renderPassEncoderIdx) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				renderPassEncoder.end();
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 */
+			wgpuRenderPassEncoderEndOcclusionQuery: (renderPassEncoderIdx) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				renderPassEncoder.endOcclusionQuery();
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number|BigInt} bundleCount
+			 * @param {number} bundlesPtr
+			 */
+			wgpuRenderPassEncoderExecuteBundles: (renderPassEncoderIdx, bundleCount, bundlesPtr) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				bundleCount = this.unwrapBigInt(bundleCount);
+				const bundles = this.array(
+					bundleCount,
+					bundlesPtr,
+					(ptr) => this.renderBundles.get(this.mem.loadPtr(ptr)),
+					4,
+				);
+				renderPassEncoder.executeBundles(bundles);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} markerLabelPtr
+			 */
+			wgpuRenderPassEncoderInsertDebugMarker: (renderPassEncoderIdx, markerLabelPtr) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				const markerLabel = this.mem.loadCstring(markerLabelPtr);
+				renderPassEncoder.insertDebugMarker(markerLabel);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 */
+			wgpuRenderPassEncoderPopDebugGroup: (renderPassEncoderIdx) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				renderPassEncoder.popDebugGroup();
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} groupLabelPtr
+			 */
+			wgpuRenderPassEncoderPushDebugGroup: (renderPassEncoderIdx, groupLabelPtr) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				const groupLabel = this.mem.loadCstring(groupLabelPtr);
+				renderPassEncoder.pushDebugGroup(groupLabel);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} groupIndex
+			 * @param {0|number} groupIdx
+			 * @param {number|BigInt} dynamicOffsetCount
+			 * @param {number} dynamicOffsetsPtr
+			 */
+			wgpuRenderPassEncoderSetBindGroup: (renderPassEncoderIdx, groupIndex, groupIdx, dynamicOffsetCount, dynamicOffsetsPtr) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+
+				let group;
+				if (groupIdx > 0) {
+					group = this.bindGroups.get(groupIdx);
+				}
+
+				dynamicOffsetCount = this.unwrapBigInt(dynamicOffsetCount);
+				const dynamicOffsets = this.array(dynamicOffsetCount, dynamicOffsetsPtr, this.mem.loadU32, 4);	
+
+				renderPassEncoder.setBindGroup(groupIndex, group, dynamicOffsets);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} bufferIdx
+			 * @param {number} formatInt
+			 * @param {BigInt} offset
+			 * @param {BigInt} size
+			 */
+			wgpuRenderPassEncoderSetIndexBuffer: (renderPassEncoderIdx, bufferIdx, formatInt, offset, size) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				const buffer = this.buffers.get(bufferIdx);
+				const format = this.enums.IndexFormat[formatInt];
+				offset = this.unwrapBigInt(offset);
+				size = this.unwrapBigInt(size);
+				renderPassEncoder.setIndexBuffer(buffer.buffer, format, offset, size);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} pipelineIdx
+			 */
+			wgpuRenderPassEncoderSetPipeline: (renderPassEncoderIdx, pipelineIdx) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				const pipeline = this.renderPipelines.get(pipelineIdx);
+				renderPassEncoder.setPipeline(pipeline);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} x
+			 * @param {number} y
+			 * @param {number} width
+			 * @param {number} height
+			 */
+			wgpuRenderPassEncoderSetScissorRect: (renderPassEncoderIdx, x, y, width, height) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				renderPassEncoder.setScissorRect(x, y, width, height);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} reference
+			 */
+			wgpuRenderPassEncoderSetStencilReference: (renderPassEncoderIdx, reference) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				renderPassEncoder.setStencilReference(reference);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} slot
+			 * @param {0|number} bufferIdx
+			 * @param {BigInt} offset
+			 * @param {BigInt} size
+			 */
+			wgpuRenderPassEncoderSetVertexBuffer: (renderPassEncoderIdx, slot, bufferIdx, offset, size) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+
+				let buffer;
+				if (bufferIdx > 0) {
+					buffer = this.buffers.get(bufferIdx).buffer;
+				}
+
+				offset = this.unwrapBigInt(offset);
+				size = this.unwrapBigInt(size);
+				renderPassEncoder.setVertexBuffer(slot, buffer, offset, size);
+			},
+
+			/**
+			 * @param {number} renderPassEncoderIdx
+			 * @param {number} x
+			 * @param {number} y
+			 * @param {number} width
+			 * @param {number} height
+			 * @param {number} minDepth
+			 * @param {number} maxDepth
+			 */
+			wgpuRenderPassEncoderSetViewport: (renderPassEncoderIdx, x, y, width, height, minDepth, maxDepth) => {
+				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
+				renderPassEncoder.setViewport(x, y, width, height, minDepth, maxDepth);
+			},
+
+			...this.renderPassEncoders.interface(true),
+
+			/* ---------------------- RenderPipeline ---------------------- */
+
+			/**
+			 * @param {number} renderPipelineIdx
+			 * @param {number} groupIndex
+			 * @returns {number}
+			 */
+			wgpuRenderPipelineGetBindGroupLayout: (renderPipelineIdx, groupIndex) => {
+				const renderPipeline = this.renderPipelines.get(renderPipelineIdx);
+				const bindGroupLayout = renderPipeline.getBindGroupLayout(groupIndex);
+				return this.bindGroupLayouts.create(bindGroupLayout);
+			},
+
+			...this.renderPipelines.interface(true),
+
+			/* ---------------------- Sampler ---------------------- */
+
+			...this.samplers.interface(true),
+
+			/* ---------------------- ShaderModule ---------------------- */
+
+			/**
+			 * @param {number} shaderModuleIdx
+			 * @param {number} callbackPtr
+			 * @param {number} userdata
+			 */
+			wgpuShaderModuleGetCompilationInfo: async (shaderModuleIdx, callbackPtr, userdata) => {
+				const shaderModule = this.shaderModules.get(shaderModuleIdx);
+				const callback = this.mem.exports.__indirect_function_table.get(callbackPtr);
+
+				let status = 0;
+				let retAddr = 0;
+
+				const ptrsToFree = [];
+
+				try {
+					const compilationInfo = await shaderModule.getCompilationInfo();
+
+					const size = compilationInfo.messages.length * 72;
+					const addr = this.mem.exports.wgpu_alloc(size);
+					ptrsToFree.push(addr);
+					compilationInfo.messages.forEach((message, i) => {
+						const messageLength = new TextEncoder().encode(message.message).length;
+						const messageAddr = this.mem.exports.wgpu_alloc(messageLength);
+						ptrsToFree.push(messageAddr);
+						this.mem.storeString(messageAddr, message.message);
+						this.mem.storeI32(addr + (i * size) + 4);
+
+						this.mem.storeI32(addr + (i * size) + 8, this.enums.CompilationMessageType.indexOf(message.type));
+
+						this.mem.storeU64(addr + (i * size) + 16, message.lineNum);
+						this.mem.storeU64(addr + (i * size) + 24, message.linePos);
+						this.mem.storeU64(addr + (i * size) + 32, message.offset);
+						this.mem.storeU64(addr + (i * size) + 40, message.length);
+
+						// TODO: UTF16 units.
+						this.mem.storeU64(addr + (i * size) + 48, message.linePos);
+						this.mem.storeU64(addr + (i * size) + 56, message.offset);
+						this.mem.storeU64(addr + (i * size) + 64, message.length);
+					});
+
+					retAddr = this.mem.exports.wgpu_alloc(3*this.mem.intSize);
+					ptrsToFree.push(retAddr);
+					this.mem.storeUint(retAddr + this.mem.intSize, compilationInfo.messages.length);
+					this.mem.storeI32(retAddr + this.mem.intSize*2, addr);
+				} catch (e) {
+					console.warn(e);
+					status = 1;
+				}
+
+				callback(status, retAddr, userdata);
+
+				ptrsToFree.forEach(ptr => this.mem.exports.wgpu_free(ptr));
+			},
+
+			...this.shaderModules.interface(true),
+
+			/* ---------------------- Surface ---------------------- */
+
+			/**
+			 * @param {number} surfaceIdx
+			 * @param {number} configPtr
+			 */
+			wgpuSurfaceConfigure: (surfaceIdx, configPtr) => {
+				const surface = this.surfaces.get(surfaceIdx);
+				const context = surface.getContext('webgpu');
+
+				const widthOff = 16 + this.mem.intSize + 8;
+				surface.width  = this.mem.loadU32(configPtr + widthOff);
+				surface.height = this.mem.loadU32(configPtr + widthOff + 4);
+
+				/** @type {GPUCanvasConfiguration} */
+				const config = {
+					device: this.devices.get(this.mem.loadPtr(configPtr + 4)),
+					format: this.enumeration("TextureFormat", configPtr + 8),
+					usage: this.mem.loadU32(configPtr + 12),
+					viewFormats: this.array(
+						this.mem.loadUint(configPtr + 16),
+						this.mem.loadPtr(configPtr + 16 + this.mem.intSize),
+						(ptr) => this.enumeration("TextureFormat", ptr),
+						4,
+					),
+					alphaMode: this.enumeration("CompositeAlphaMode", configPtr + widthOff - 4),
+					// // NOTE: present mode seems unused.
+					presentMode: this.enumeration("PresentMode", configPtr + widthOff + 4),
+				};
+
+				context.configure(config);
+			},
+
+			/**
+			 * @param {number} surfaceIdx
+			 * @param {number} adapterIdx
+			 * @param {number} capabilitiesPtr
+			 */
+			wgpuSurfaceGetCapabilities: (surfaceIdx, adapterIdx, capabilitiesPtr) => {
+				const formatStr = navigator.gpu.getPreferredCanvasFormat();
+				const format = this.enums.TextureFormat.indexOf(formatStr);
+
+				this.mem.storeUint(capabilitiesPtr + this.mem.intSize, 1);
+				const formatAddr = this.mem.exports.wgpu_alloc(4);
+				this.mem.storeI32(formatAddr, format);
+				this.mem.storeI32(capabilitiesPtr + this.mem.intSize*2, formatAddr);
+
+				// NOTE: present modes don't seem to actually do anything in JS, we can just give back a default FIFO though.
+				this.mem.storeUint(capabilitiesPtr + this.mem.intSize*3, 1);
+				const presentModesAddr = this.mem.exports.wgpu_alloc(4);
+				this.mem.storeI32(presentModesAddr, 0);
+				this.mem.storeI32(capabilitiesPtr + this.mem.intSize*4, presentModesAddr);
+
+				// Browser seems to support opaque (1) and premultiplied (2).
+				this.mem.storeUint(capabilitiesPtr + this.mem.intSize*5, 2);
+				const alphaModesAddr = this.mem.exports.wgpu_alloc(8);
+				this.mem.storeI32(alphaModesAddr + 0, 1); // Opaque.
+				this.mem.storeI32(alphaModesAddr + 4, 2); // premultiplied.
+				this.mem.storeI32(capabilitiesPtr + this.mem.intSize*6, alphaModesAddr);
+			},
+
+			/**
+			 * @param {number} surfaceIdx
+			 * @param {number} texturePtr
+			 */
+			wgpuSurfaceGetCurrentTexture: (surfaceIdx, texturePtr) => {
+				const surface = this.surfaces.get(surfaceIdx);
+				const context = surface.getContext('webgpu');
+				const texture = context.getCurrentTexture();
+
+				const textureIdx = this.textures.create(texture);
+				this.mem.storeI32(texturePtr, textureIdx);
+
+				// TODO: determine suboptimal and/or status.
+			},
+
+			/**
+			 * @param {number} surfaceIdx
+			 * @param {number} texturePtr
+			 * @returns {number}
+			 */
+			wgpuSurfaceGetPreferredFormat: (surfaceIdx, adapterIdx) => {
+				const formatStr = navigator.gpu.getPreferredCanvasFormat();
+				const format = this.enums.TextureFormat.indexOf(formatStr);
+				return format;
+			},
+
+			/**
+			 * @param {number} surfaceIdx
+			 */
+			wgpuSurfacePresent: (surfaceIdx) => {
+				// NOTE: Not really anything to do here.
+			},
+
+			/**
+			 * @param {number} surfaceIdx
+			 */
+			wgpuSurfaceUnconfigure: (surfaceIdx) => {
+				const surface = this.surfaces.get(surfaceIdx);
+				surface.getContext('webgpu').unconfigure();
+			},
+
+			...this.surfaces.interface(true),
+
+			/* ---------------------- SurfaceCapabilities ---------------------- */
+
+			/**
+			 * @param {number} surfaceCapabilitiesPtr
+			 */
+			wgpuSurfaceCapabilitiesFreeMembers: (surfaceCapabilitiesPtr) => {
+				const formatsAddr = this.mem.loadI32(surfaceCapabilitiesPtr + this.mem.intSize*2);
+				this.mem.exports.wgpu_free(formatsAddr);
+
+				const presentModesAddr = this.mem.loadI32(surfaceCapabilitiesPtr + this.mem.intSize*4);
+				this.mem.exports.wgpu_free(presentModesAddr);
+
+				const alphaModesAddr = this.mem.loadI32(surfaceCapabilitiesPtr + this.mem.intSize*6);
+				this.mem.exports.wgpu_free(alphaModesAddr);
+			},
+
+			/* ---------------------- Texture ---------------------- */
+			
+			/**
+			 * @param {number} textureIdx
+			 * @param {0|number} descriptorPtr
+			 * @returns {number}
+			 */
+			wgpuTextureCreateView: (textureIdx, descriptorPtr) => {
+				const texture = this.textures.get(textureIdx);
+
+				/** @type {?GPUTextureViewDescriptor} */
+				let descriptor;
+				if (descriptorPtr != 0) {
+					descriptor = {
+						label:           this.mem.loadCstring(descriptorPtr + 4),
+						format:          this.enumeration("TextureFormat", descriptorPtr + 8),
+						dimension:       this.enumeration("TextureViewDimension", descriptorPtr + 12),
+						baseMipLevel:    this.mem.loadU32(descriptorPtr + 16),
+						mipLevelCount:   this.mem.loadU32(descriptorPtr + 20),
+						baseArrayLayer:  this.mem.loadU32(descriptorPtr + 24),
+						arrayLayerCount: this.mem.loadU32(descriptorPtr + 28),
+						aspect:          this.enumeration("TextureAspect", descriptorPtr + 32),
+					};
+					if (descriptor.arrayLayerCount == 0xFFFFFFFF) {
+						descriptor.arrayLayerCount = undefined;
+					}
+					if (descriptor.mipLevelCount == 0xFFFFFFFF) {
+						descriptor.mipLevelCount = undefined;
+					}
+				}
+
+				const textureView = texture.createView(descriptor);
+				return this.textureViews.create(textureView);
+			},
+
+			/**
+			 * @param {number} textureIdx
+			 */
+			wgpuTextureDestroy: (textureIdx) => {
+				const texture = this.textures.get(textureIdx);
+				texture.destroy();
+			},
+
+			/**
+			 * @param {number} textureIdx
+			 * @returns {number}
+			 */
+			wgpuTextureDepthOrArrayLayers: (textureIdx) => {
+				const texture = this.textures.get(textureIdx);
+				return texture.depthOrArrayLayers;
+			},
+
+			/**
+			 * @param {number} textureIdx
+			 * @returns {number}
+			 */
+			wgpuTextureGetDimension: (textureIdx) => {
+				const texture = this.textures.get(textureIdx);
+				return this.enums.TextureDimension.indexOf(texture.dimension);
+			},
+
+			/**
+			 * @param {number} textureIdx
+			 * @returns {number}
+			 */
+			wgpuTextureGetFormat: (textureIdx) => {
+				const texture = this.textures.get(textureIdx);
+				return this.enums.TextureFormat.indexOf(texture.format);
+			},
+
+			/**
+			 * @param {number} textureIdx
+			 * @returns {number}
+			 */
+			wgpuTextureGetHeight: (textureIdx) => {
+				const texture = this.textures.get(textureIdx);
+				return texture.height;
+			},
+
+			/**
+			 * @param {number} textureIdx
+			 * @returns {number}
+			 */
+			wgpuTextureGetMipLevelCount: (textureIdx) => {
+				const texture = this.textures.get(textureIdx);
+				return texture.mipLevelCount;
+			},
+
+			/**
+			 * @param {number} textureIdx
+			 * @returns {number}
+			 */
+			wgpuTextureGetSampleCount: (textureIdx) => {
+				const texture = this.textures.get(textureIdx);
+				return texture.sampleCount;
+			},
+
+			/**
+			 * @param {number} textureIdx
+			 * @returns {number}
+			 */
+			wgpuTextureGetUsage: (textureIdx) => {
+				const texture = this.textures.get(textureIdx);
+				return texture.usage;
+			},
+
+			/**
+			 * @param {number} textureIdx
+			 * @returns {number}
+			 */
+			wgpuTextureGetWidth: (textureIdx) => {
+				const texture = this.textures.get(textureIdx);
+				return texture.width;
+			},
+
+			...this.textures.interface(true),
+
+			/* ---------------------- TextureView ---------------------- */
+
+			...this.textureViews.interface(true),
+		};
+	}
+}
+
+/** @template T */
+class WebGPUObjectManager {
+
+	/**
+	 * @param {string} name
+	 * @param {WasmMemoryInterface} mem
+	 */
+	constructor(name, mem) {
+		this.name = name;
+		this.mem = mem;
+
+		this.idx = 0;
+
+		/** @type {Record<number, { references: number, object: T }>} */
+		this.objects = {};
+	}
+
+	/**
+	 * @param {T} object
+	 * @returns {number}
+	 */
+	create(object) {
+		this.objects[this.idx] = { references: 1, object };
+		this.idx += 1;
+		return this.idx;
+	}
+
+	/**
+	 * @param {number} idx
+	 * @returns {T}
+	 */
+	get(idx) {
+		return this.objects[idx-1].object;
+	}
+
+	/** @param {number} idx */
+	release(idx) {
+		this.objects[idx-1].references -= 1;
+		if (this.objects[idx-1].references == 0) {
+			delete this.objects[idx-1];
+		}
+	}
+
+	/** @param {number} idx */
+	reference(idx) {
+		this.objects[idx-1].references += 1;
+	}
+	
+	interface(withLabelSetter = false) {
+		const inter = {};
+		inter[`wgpu${this.name}Reference`] = this.reference.bind(this);
+		inter[`wgpu${this.name}Release`] = this.release.bind(this);
+		if (withLabelSetter) {
+			inter[`wgpu${this.name}SetLabel`] = (idx, labelPtr) => {
+				const obj = this.get(idx);
+				obj.label = this.mem.loadCstring(labelPtr);
+			};
+		}
+		return inter;
+	}
+}
+
+window.odin = window.odin || {};
+window.odin.WebGPUInterface = WebGPUInterface;
+
+})();

+ 1636 - 0
vendor/wgpu/wgpu.odin

@@ -0,0 +1,1636 @@
+package wgpu
+
+import "base:intrinsics"
+
+WGPU_SHARED :: #config(WGPU_SHARED, false)
+WGPU_DEBUG  :: #config(WGPU_DEBUG,  false)
+
+@(private) TYPE :: "debug" when WGPU_DEBUG else "release"
+
+when ODIN_OS == .Windows {
+	@(private) ARCH :: "x86_64"   when ODIN_ARCH == .amd64 else "i686" when ODIN_ARCH == .i386 else #panic("unsupported WGPU Native architecture")
+	@(private) EXT  :: ".dll.lib" when WGPU_SHARED else ".lib"
+	@(private) LIB  :: "lib/wgpu-windows-" + ARCH + "-" + TYPE + "/wgpu-native" + EXT
+
+	when !#exists(LIB) {
+		#panic("Could not find the compiled WGPU Native library at '" + ODIN_ROOT + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v0.19.4.1, make sure to read the README at '" + ODIN_ROOT + "vendor/wgpu/README.md'")
+	}
+
+	foreign import libwgpu {
+		LIB,
+		"system:d3dcompiler.lib",
+		"system:ws2_32.lib",
+		"system:userenv.lib",
+		"system:bcrypt.lib",
+		"system:ntdll.lib",
+		"system:opengl32.lib",
+		"system:advapi32.lib",
+	}
+} else when ODIN_OS == .Darwin {
+	@(private) ARCH :: "x86_64" when ODIN_ARCH == .amd64 else "aarch64" when ODIN_ARCH == .arm64 else #panic("unsupported WGPU Native architecture")
+	@(private) EXT  :: ".dylib" when WGPU_SHARED else ".a"
+	@(private) LIB  :: "lib/wgpu-macos-" + ARCH + "-" + TYPE + "/libwgpu_native" + EXT
+
+	when !#exists(LIB) {
+		#panic("Could not find the compiled WGPU Native library at '" + ODIN_ROOT + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v0.19.4.1, make sure to read the README at '" + ODIN_ROOT + "vendor/wgpu/README.md'")
+	}
+
+	foreign import libwgpu {
+		LIB,
+		"system:CoreFoundation.framework",
+		"system:QuartzCore.framework",
+		"system:Metal.framework",
+	}
+} else when ODIN_OS == .Linux {
+	@(private) ARCH :: "x86_64" when ODIN_ARCH == .amd64 else "aarch64" when ODIN_ARCH == .arm64 else #panic("unsupported WGPU Native architecture")
+	@(private) EXT  :: ".so"    when WGPU_SHARED else ".a"
+	@(private) LIB  :: "lib/wgpu-linux-" + ARCH + "-" + TYPE + "/libwgpu_native" + EXT
+
+	when !#exists(LIB) {
+		#panic("Could not find the compiled WGPU Native library at '" + ODIN_ROOT + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v0.19.4.1, make sure to read the README at '" + ODIN_ROOT + "vendor/wgpu/README.md'")
+	}
+
+	foreign import libwgpu {
+		LIB,
+		"system:ld",
+		"system:m",
+	}
+} else when ODIN_OS == .JS {
+	foreign import libwgpu "wgpu"
+}
+
+ARRAY_LAYER_COUNT_UNDEFINED :: max(u32)
+COPY_STRIDE_UNDEFINED :: max(u32)
+DEPTH_SLICE_UNDEFINED :: max(u32)
+LIMIT_U32_UNDEFINED :: max(u32)
+LIMIT_U64_UNDEFINED :: max(u64)
+MIP_LEVEL_COUNT_UNDEFINED :: max(u32)
+QUERY_SET_INDEX_UNDEFINED :: max(u32)
+WHOLE_MAP_SIZE :: max(uint)
+WHOLE_SIZE :: max(u64)
+
+Flags :: u32
+
+Adapter :: distinct rawptr
+BindGroup :: distinct rawptr
+BindGroupLayout :: distinct rawptr
+Buffer :: distinct rawptr
+CommandBuffer :: distinct rawptr
+CommandEncoder :: distinct rawptr
+ComputePassEncoder :: distinct rawptr
+ComputePipeline :: distinct rawptr
+Device :: distinct rawptr
+Instance :: distinct rawptr
+PipelineLayout :: distinct rawptr
+QuerySet :: distinct rawptr
+Queue :: distinct rawptr
+RenderBundle :: distinct rawptr
+RenderBundleEncoder :: distinct rawptr
+RenderPassEncoder :: distinct rawptr
+RenderPipeline :: distinct rawptr
+Sampler :: distinct rawptr
+ShaderModule :: distinct rawptr
+Surface :: distinct rawptr
+Texture :: distinct rawptr
+TextureView :: distinct rawptr
+
+AdapterType :: enum i32 {
+	DiscreteGPU = 0x00000000,
+	IntegratedGPU = 0x00000001,
+	CPU = 0x00000002,
+	Unknown = 0x00000003,
+}
+
+AddressMode :: enum i32 {
+	Repeat = 0x00000000,
+	MirrorRepeat = 0x00000001,
+	ClampToEdge = 0x00000002,
+}
+
+BackendType :: enum i32 {
+	Undefined = 0x00000000,
+	Null = 0x00000001,
+	WebGPU = 0x00000002,
+	D3D11 = 0x00000003,
+	D3D12 = 0x00000004,
+	Metal = 0x00000005,
+	Vulkan = 0x00000006,
+	OpenGL = 0x00000007,
+	OpenGLES = 0x00000008,
+}
+
+BlendFactor :: enum i32 {
+	Zero = 0x00000000,
+	One = 0x00000001,
+	Src = 0x00000002,
+	OneMinusSrc = 0x00000003,
+	SrcAlpha = 0x00000004,
+	OneMinusSrcAlpha = 0x00000005,
+	Dst = 0x00000006,
+	OneMinusDst = 0x00000007,
+	DstAlpha = 0x00000008,
+	OneMinusDstAlpha = 0x00000009,
+	SrcAlphaSaturated = 0x0000000A,
+	Constant = 0x0000000B,
+	OneMinusConstant = 0x0000000C,
+}
+
+BlendOperation :: enum i32 {
+	Add = 0x00000000,
+	Subtract = 0x00000001,
+	ReverseSubtract = 0x00000002,
+	Min = 0x00000003,
+	Max = 0x00000004,
+}
+
+BufferBindingType :: enum i32 {
+	Undefined = 0x00000000,
+	Uniform = 0x00000001,
+	Storage = 0x00000002,
+	ReadOnlyStorage = 0x00000003,
+}
+
+BufferMapAsyncStatus :: enum i32 {
+	Success = 0x00000000,
+	ValidationError = 0x00000001,
+	Unknown = 0x00000002,
+	DeviceLost = 0x00000003,
+	DestroyedBeforeCallback = 0x00000004,
+	UnmappedBeforeCallback = 0x00000005,
+	MappingAlreadyPending = 0x00000006,
+	OffsetOutOfRange = 0x00000007,
+	SizeOutOfRange = 0x00000008,
+}
+
+BufferMapState :: enum i32 {
+	Unmapped = 0x00000000,
+	Pending = 0x00000001,
+	Mapped = 0x00000002,
+}
+
+CompareFunction :: enum i32 {
+	Undefined = 0x00000000,
+	Never = 0x00000001,
+	Less = 0x00000002,
+	LessEqual = 0x00000003,
+	Greater = 0x00000004,
+	GreaterEqual = 0x00000005,
+	Equal = 0x00000006,
+	NotEqual = 0x00000007,
+	Always = 0x00000008,
+}
+
+CompilationInfoRequestStatus :: enum i32 {
+	Success = 0x00000000,
+	Error = 0x00000001,
+	DeviceLost = 0x00000002,
+	Unknown = 0x00000003,
+}
+
+CompilationMessageType :: enum i32 {
+	Error = 0x00000000,
+	Warning = 0x00000001,
+	Info = 0x00000002,
+}
+
+CompositeAlphaMode :: enum i32 {
+	Auto = 0x00000000,
+	Opaque = 0x00000001,
+	Premultiplied = 0x00000002,
+	Unpremultiplied = 0x00000003,
+	Inherit = 0x00000004,
+}
+
+CreatePipelineAsyncStatus :: enum i32 {
+	Success = 0x00000000,
+	ValidationError = 0x00000001,
+	InternalError = 0x00000002,
+	DeviceLost = 0x00000003,
+	DeviceDestroyed = 0x00000004,
+	Unknown = 0x00000005,
+}
+
+CullMode :: enum i32 {
+	None = 0x00000000,
+	Front = 0x00000001,
+	Back = 0x00000002,
+}
+
+DeviceLostReason :: enum i32 {
+	Undefined = 0x00000000,
+	Destroyed = 0x00000001,
+}
+
+ErrorFilter :: enum i32 {
+	Validation = 0x00000000,
+	OutOfMemory = 0x00000001,
+	Internal = 0x00000002,
+}
+
+ErrorType :: enum i32 {
+	NoError = 0x00000000,
+	Validation = 0x00000001,
+	OutOfMemory = 0x00000002,
+	Internal = 0x00000003,
+	Unknown = 0x00000004,
+	DeviceLost = 0x00000005,
+}
+
+FeatureName :: enum i32 {
+	// WebGPU.
+	Undefined = 0x00000000,
+	DepthClipControl = 0x00000001,
+	Depth32FloatStencil8 = 0x00000002,
+	TimestampQuery = 0x00000003,
+	TextureCompressionBC = 0x00000004,
+	TextureCompressionETC2 = 0x00000005,
+	TextureCompressionASTC = 0x00000006,
+	IndirectFirstInstance = 0x00000007,
+	ShaderF16 = 0x00000008,
+	RG11B10UfloatRenderable = 0x00000009,
+	BGRA8UnormStorage = 0x0000000A,
+	Float32Filterable = 0x0000000B,
+
+	// Native.
+	PushConstants = 0x00030001,
+	TextureAdapterSpecificFormatFeatures,
+	MultiDrawIndirect,
+	MultiDrawIndirectCount,
+	VertexWritableStorage,
+	TextureBindingArray,
+	SampledTextureAndStorageBufferArrayNonUniformIndexing,
+	PipelineStatisticsQuery,
+	StorageResourceBindingArray,
+	PartiallyBoundBindingArray,
+}
+
+FilterMode :: enum i32 {
+	Nearest = 0x00000000,
+	Linear = 0x00000001,
+}
+
+FrontFace :: enum i32 {
+	CCW = 0x00000000,
+	CW = 0x00000001,
+}
+
+IndexFormat :: enum i32 {
+	Undefined = 0x00000000,
+	Uint16 = 0x00000001,
+	Uint32 = 0x00000002,
+}
+
+LoadOp :: enum i32 {
+	Undefined = 0x00000000,
+	Clear = 0x00000001,
+	Load = 0x00000002,
+}
+
+MipmapFilterMode :: enum i32 {
+	Nearest = 0x00000000,
+	Linear = 0x00000001,
+}
+
+PowerPreference :: enum i32 {
+	Undefined = 0x00000000,
+	LowPower = 0x00000001,
+	HighPerformance = 0x00000002,
+}
+
+PresentMode :: enum i32 {
+	Fifo = 0x00000000,
+	FifoRelaxed = 0x00000001,
+	Immediate = 0x00000002,
+	Mailbox = 0x00000003,
+}
+
+PrimitiveTopology :: enum i32 {
+	PointList = 0x00000000,
+	LineList = 0x00000001,
+	LineStrip = 0x00000002,
+	TriangleList = 0x00000003,
+	TriangleStrip = 0x00000004,
+}
+
+QueryType :: enum i32 {
+	// WebGPU.
+	Occlusion = 0x00000000,
+	Timestamp = 0x00000001,
+
+	// Native.
+	PipelineStatistics = 0x00030000,
+}
+
+QueueWorkDoneStatus :: enum i32 {
+	Success = 0x00000000,
+	Error = 0x00000001,
+	Unknown = 0x00000002,
+	DeviceLost = 0x00000003,
+}
+
+RequestAdapterStatus :: enum i32 {
+	Success = 0x00000000,
+	Unavailable = 0x00000001,
+	Error = 0x00000002,
+	Unknown = 0x00000003,
+}
+
+RequestDeviceStatus :: enum i32 {
+	Success = 0x00000000,
+	Error = 0x00000001,
+	Unknown = 0x00000002,
+}
+
+SType :: enum i32 {
+	// WebGPU.
+	Invalid = 0x00000000,
+	SurfaceDescriptorFromMetalLayer = 0x00000001,
+	SurfaceDescriptorFromWindowsHWND = 0x00000002,
+	SurfaceDescriptorFromXlibWindow = 0x00000003,
+	SurfaceDescriptorFromCanvasHTMLSelector = 0x00000004,
+	ShaderModuleSPIRVDescriptor = 0x00000005,
+	ShaderModuleWGSLDescriptor = 0x00000006,
+	PrimitiveDepthClipControl = 0x00000007,
+	SurfaceDescriptorFromWaylandSurface = 0x00000008,
+	SurfaceDescriptorFromAndroidNativeWindow = 0x00000009,
+	SurfaceDescriptorFromXcbWindow = 0x0000000A,
+	RenderPassDescriptorMaxDrawCount = 0x0000000F,
+
+	// Native.
+	DeviceExtras = 0x00030001,
+	RequiredLimitsExtras,
+	PipelineLayoutExtras,
+	ShaderModuleGLSLDescriptor,
+	SupportedLimitsExtras,
+	InstanceExtras,
+	BindGroupEntryExtras,
+	BindGroupLayoutEntryExtras,
+	QuerySetDescriptorExtras,
+	SurfaceConfigurationExtras,
+}
+
+SamplerBindingType :: enum i32 {
+	Undefined = 0x00000000,
+	Filtering = 0x00000001,
+	NonFiltering = 0x00000002,
+	Comparison = 0x00000003,
+}
+
+StencilOperation :: enum i32 {
+	Keep = 0x00000000,
+	Zero = 0x00000001,
+	Replace = 0x00000002,
+	Invert = 0x00000003,
+	IncrementClamp = 0x00000004,
+	DecrementClamp = 0x00000005,
+	IncrementWrap = 0x00000006,
+	DecrementWrap = 0x00000007,
+}
+
+StorageTextureAccess :: enum i32 {
+	Undefined = 0x00000000,
+	WriteOnly = 0x00000001,
+	ReadOnly = 0x00000002,
+	ReadWrite = 0x00000003,
+}
+
+StoreOp :: enum i32 {
+	Undefined = 0x00000000,
+	Store = 0x00000001,
+	Discard = 0x00000002,
+}
+
+SurfaceGetCurrentTextureStatus :: enum i32 {
+	Success = 0x00000000,
+	Timeout = 0x00000001,
+	Outdated = 0x00000002,
+	Lost = 0x00000003,
+	OutOfMemory = 0x00000004,
+	DeviceLost = 0x00000005,
+}
+
+TextureAspect :: enum i32 {
+	All = 0x00000000,
+	StencilOnly = 0x00000001,
+	DepthOnly = 0x00000002,
+}
+
+TextureDimension :: enum i32 {
+	_1D = 0x00000000,
+	_2D = 0x00000001,
+	_3D = 0x00000002,
+}
+
+TextureFormat :: enum i32 {
+	Undefined = 0x00000000,
+	R8Unorm = 0x00000001,
+	R8Snorm = 0x00000002,
+	R8Uint = 0x00000003,
+	R8Sint = 0x00000004,
+	R16Uint = 0x00000005,
+	R16Sint = 0x00000006,
+	R16Float = 0x00000007,
+	RG8Unorm = 0x00000008,
+	RG8Snorm = 0x00000009,
+	RG8Uint = 0x0000000A,
+	RG8Sint = 0x0000000B,
+	R32Float = 0x0000000C,
+	R32Uint = 0x0000000D,
+	R32Sint = 0x0000000E,
+	RG16Uint = 0x0000000F,
+	RG16Sint = 0x00000010,
+	RG16Float = 0x00000011,
+	RGBA8Unorm = 0x00000012,
+	RGBA8UnormSrgb = 0x00000013,
+	RGBA8Snorm = 0x00000014,
+	RGBA8Uint = 0x00000015,
+	RGBA8Sint = 0x00000016,
+	BGRA8Unorm = 0x00000017,
+	BGRA8UnormSrgb = 0x00000018,
+	RGB10A2Uint = 0x00000019,
+	RGB10A2Unorm = 0x0000001A,
+	RG11B10Ufloat = 0x0000001B,
+	RGB9E5Ufloat = 0x0000001C,
+	RG32Float = 0x0000001D,
+	RG32Uint = 0x0000001E,
+	RG32Sint = 0x0000001F,
+	RGBA16Uint = 0x00000020,
+	RGBA16Sint = 0x00000021,
+	RGBA16Float = 0x00000022,
+	RGBA32Float = 0x00000023,
+	RGBA32Uint = 0x00000024,
+	RGBA32Sint = 0x00000025,
+	Stencil8 = 0x00000026,
+	Depth16Unorm = 0x00000027,
+	Depth24Plus = 0x00000028,
+	Depth24PlusStencil8 = 0x00000029,
+	Depth32Float = 0x0000002A,
+	Depth32FloatStencil8 = 0x0000002B,
+	BC1RGBAUnorm = 0x0000002C,
+	BC1RGBAUnormSrgb = 0x0000002D,
+	BC2RGBAUnorm = 0x0000002E,
+	BC2RGBAUnormSrgb = 0x0000002F,
+	BC3RGBAUnorm = 0x00000030,
+	BC3RGBAUnormSrgb = 0x00000031,
+	BC4RUnorm = 0x00000032,
+	BC4RSnorm = 0x00000033,
+	BC5RGUnorm = 0x00000034,
+	BC5RGSnorm = 0x00000035,
+	BC6HRGBUfloat = 0x00000036,
+	BC6HRGBFloat = 0x00000037,
+	BC7RGBAUnorm = 0x00000038,
+	BC7RGBAUnormSrgb = 0x00000039,
+	ETC2RGB8Unorm = 0x0000003A,
+	ETC2RGB8UnormSrgb = 0x0000003B,
+	ETC2RGB8A1Unorm = 0x0000003C,
+	ETC2RGB8A1UnormSrgb = 0x0000003D,
+	ETC2RGBA8Unorm = 0x0000003E,
+	ETC2RGBA8UnormSrgb = 0x0000003F,
+	EACR11Unorm = 0x00000040,
+	EACR11Snorm = 0x00000041,
+	EACRG11Unorm = 0x00000042,
+	EACRG11Snorm = 0x00000043,
+	ASTC4x4Unorm = 0x00000044,
+	ASTC4x4UnormSrgb = 0x00000045,
+	ASTC5x4Unorm = 0x00000046,
+	ASTC5x4UnormSrgb = 0x00000047,
+	ASTC5x5Unorm = 0x00000048,
+	ASTC5x5UnormSrgb = 0x00000049,
+	ASTC6x5Unorm = 0x0000004A,
+	ASTC6x5UnormSrgb = 0x0000004B,
+	ASTC6x6Unorm = 0x0000004C,
+	ASTC6x6UnormSrgb = 0x0000004D,
+	ASTC8x5Unorm = 0x0000004E,
+	ASTC8x5UnormSrgb = 0x0000004F,
+	ASTC8x6Unorm = 0x00000050,
+	ASTC8x6UnormSrgb = 0x00000051,
+	ASTC8x8Unorm = 0x00000052,
+	ASTC8x8UnormSrgb = 0x00000053,
+	ASTC10x5Unorm = 0x00000054,
+	ASTC10x5UnormSrgb = 0x00000055,
+	ASTC10x6Unorm = 0x00000056,
+	ASTC10x6UnormSrgb = 0x00000057,
+	ASTC10x8Unorm = 0x00000058,
+	ASTC10x8UnormSrgb = 0x00000059,
+	ASTC10x10Unorm = 0x0000005A,
+	ASTC10x10UnormSrgb = 0x0000005B,
+	ASTC12x10Unorm = 0x0000005C,
+	ASTC12x10UnormSrgb = 0x0000005D,
+	ASTC12x12Unorm = 0x0000005E,
+	ASTC12x12UnormSrgb = 0x0000005F,
+}
+
+TextureSampleType :: enum i32 {
+	Undefined = 0x00000000,
+	Float = 0x00000001,
+	UnfilterableFloat = 0x00000002,
+	Depth = 0x00000003,
+	Sint = 0x00000004,
+	Uint = 0x00000005,
+}
+
+TextureViewDimension :: enum i32 {
+	Undefined = 0x00000000,
+	_1D = 0x00000001,
+	_2D = 0x00000002,
+	_2DArray = 0x00000003,
+	Cube = 0x00000004,
+	CubeArray = 0x00000005,
+	_3D = 0x00000006,
+}
+
+VertexFormat :: enum i32 {
+	Undefined = 0x00000000,
+	Uint8x2 = 0x00000001,
+	Uint8x4 = 0x00000002,
+	Sint8x2 = 0x00000003,
+	Sint8x4 = 0x00000004,
+	Unorm8x2 = 0x00000005,
+	Unorm8x4 = 0x00000006,
+	Snorm8x2 = 0x00000007,
+	Snorm8x4 = 0x00000008,
+	Uint16x2 = 0x00000009,
+	Uint16x4 = 0x0000000A,
+	Sint16x2 = 0x0000000B,
+	Sint16x4 = 0x0000000C,
+	Unorm16x2 = 0x0000000D,
+	Unorm16x4 = 0x0000000E,
+	Snorm16x2 = 0x0000000F,
+	Snorm16x4 = 0x00000010,
+	Float16x2 = 0x00000011,
+	Float16x4 = 0x00000012,
+	Float32 = 0x00000013,
+	Float32x2 = 0x00000014,
+	Float32x3 = 0x00000015,
+	Float32x4 = 0x00000016,
+	Uint32 = 0x00000017,
+	Uint32x2 = 0x00000018,
+	Uint32x3 = 0x00000019,
+	Uint32x4 = 0x0000001A,
+	Sint32 = 0x0000001B,
+	Sint32x2 = 0x0000001C,
+	Sint32x3 = 0x0000001D,
+	Sint32x4 = 0x0000001E,
+}
+
+VertexStepMode :: enum i32 {
+	Vertex = 0x00000000,
+	Instance = 0x00000001,
+	VertexBufferNotUsed = 0x00000002,
+}
+
+// WGSLFeatureName :: enum i32 {
+//     Undefined = 0x00000000,
+//     ReadonlyAndReadwriteStorageTextures = 0x00000001,
+//     Packed4x8IntegerDotProduct = 0x00000002,
+//     UnrestrictedPointerParameters = 0x00000003,
+//     PointerCompositeAccess = 0x00000004,
+// }
+
+BufferUsage :: enum i32 {
+	MapRead = 0x00000000,
+	MapWrite = 0x00000001,
+	CopySrc = 0x00000002,
+	CopyDst = 0x00000003,
+	Index = 0x00000004,
+	Vertex = 0x00000005,
+	Uniform = 0x00000006,
+	Storage = 0x00000007,
+	Indirect = 0x00000008,
+	QueryResolve = 0x00000009,
+}
+BufferUsageFlags :: bit_set[BufferUsage; Flags]
+
+ColorWriteMask :: enum i32 {
+	Red = 0x00000000,
+	Green = 0x00000001,
+	Blue = 0x00000002,
+	Alpha = 0x00000003,
+}
+ColorWriteMaskFlags :: bit_set[ColorWriteMask; Flags]
+ColorWriteMaskFlags_All :: ColorWriteMaskFlags{ .Red, .Green, .Blue, .Alpha }
+
+MapMode :: enum i32 {
+	Read = 0x00000000,
+	Write = 0x00000001,
+}
+MapModeFlags :: bit_set[MapMode; Flags]
+
+ShaderStage :: enum i32 {
+	Vertex = 0x00000000,
+	Fragment = 0x00000001,
+	Compute = 0x00000002,
+}
+ShaderStageFlags :: bit_set[ShaderStage; Flags]
+
+TextureUsage :: enum i32 {
+	CopySrc = 0x00000000,
+	CopyDst = 0x00000001,
+	TextureBinding = 0x00000002,
+	StorageBinding = 0x00000003,
+	RenderAttachment = 0x00000004,
+}
+TextureUsageFlags :: bit_set[TextureUsage; Flags]
+
+
+BufferMapAsyncCallback :: #type proc "c" (status: BufferMapAsyncStatus, /* NULLABLE */ userdata: rawptr)
+ShaderModuleGetCompilationInfoCallback :: #type proc "c" (status: CompilationInfoRequestStatus, compilationInfo: ^CompilationInfo, /* NULLABLE */ userdata: rawptr)
+DeviceCreateComputePipelineAsyncCallback :: #type proc "c" (status: CreatePipelineAsyncStatus, pipeline: ComputePipeline, message: cstring, /* NULLABLE */ userdata: rawptr)
+DeviceCreateRenderPipelineAsyncCallback :: #type proc "c" (status: CreatePipelineAsyncStatus, pipeline: RenderPipeline, message: cstring, /* NULLABLE */ userdata: rawptr)
+
+DeviceLostCallback :: #type proc "c" (reason: DeviceLostReason, message: cstring, userdata: rawptr)
+ErrorCallback :: #type proc "c" (type: ErrorType, message: cstring, userdata: rawptr)
+
+Proc :: distinct rawptr
+
+QueueOnSubmittedWorkDoneCallback :: #type proc "c" (status: QueueWorkDoneStatus, /* NULLABLE */ userdata: rawptr)
+InstanceRequestAdapterCallback :: #type proc "c" (status: RequestAdapterStatus, adapter: Adapter, message: cstring, /* NULLABLE */ userdata: rawptr)
+AdapterRequestDeviceCallback :: #type proc "c" (status: RequestDeviceStatus, device: Device, message: cstring, /* NULLABLE */ userdata: rawptr)
+
+// AdapterRequestAdapterInfoCallback :: #type proc "c" (adapterInfo: AdapterInfo, /* NULLABLE */ userdata: rawptr)
+
+ChainedStruct :: struct {
+	next:  ^ChainedStruct,
+	sType: SType,
+}
+
+ChainedStructOut :: struct {
+	next:  ^ChainedStructOut,
+	sType: SType,
+}
+
+// AdapterInfo :: struct {
+// 	next: ^ChainedStructOut,
+//     vendor: cstring,
+//     architecture: cstring,
+//     device: cstring,
+//     description: cstring,
+// 	backendType: BackendType,
+// 	adapterType: AdapterType,
+// 	vendorID: u32,
+// 	deviceID: u32,
+// }
+
+AdapterProperties :: struct {
+	nextInChain: ^ChainedStructOut,
+	vendorID: u32,
+	vendorName: cstring,
+	architecture: cstring,
+	deviceID: u32,
+	name: cstring,
+	driverDescription: cstring,
+	adapterType: AdapterType,
+	backendType: BackendType,
+}
+
+BindGroupEntry :: struct {
+	nextInChain: ^ChainedStruct,
+	binding: u32,
+	/* NULLABLE */ buffer: Buffer,
+	offset: u64,
+	size: u64,
+	/* NULLABLE */ sampler: Sampler,
+	/* NULLABLE */ textureView: TextureView,
+}
+
+BlendComponent :: struct {
+	operation: BlendOperation,
+	srcFactor: BlendFactor,
+	dstFactor: BlendFactor,
+}
+
+BufferBindingLayout :: struct {
+	nextInChain: ^ChainedStruct,
+	type: BufferBindingType,
+	hasDynamicOffset: b32,
+	minBindingSize: u64,
+}
+
+BufferDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	usage: BufferUsageFlags,
+	size: u64,
+	mappedAtCreation: b32,
+}
+
+Color :: struct {
+	r: f64,
+	g: f64,
+	b: f64,
+	a: f64,
+}
+
+CommandBufferDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+}
+
+CommandEncoderDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+}
+
+CompilationMessage :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ message: cstring,
+	type: CompilationMessageType,
+	lineNum: u64,
+	linePos: u64,
+	offset: u64,
+	length: u64,
+	utf16LinePos: u64,
+	utf16Offset: u64,
+	utf16Length: u64,
+}
+
+ComputePassTimestampWrites :: struct {
+	querySet: QuerySet,
+	beginningOfPassWriteIndex: u32,
+	endOfPassWriteIndex: u32,
+}
+
+ConstantEntry :: struct {
+	nextInChain: ^ChainedStruct,
+	key: cstring,
+	value: f64,
+}
+
+Extent3D :: struct {
+	width: u32,
+	height: u32,
+	depthOrArrayLayers: u32,
+}
+
+InstanceDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+}
+
+Limits :: struct {
+	maxTextureDimension1D: u32,
+	maxTextureDimension2D: u32,
+	maxTextureDimension3D: u32,
+	maxTextureArrayLayers: u32,
+	maxBindGroups: u32,
+	maxBindGroupsPlusVertexBuffers: u32,
+	maxBindingsPerBindGroup: u32,
+	maxDynamicUniformBuffersPerPipelineLayout: u32,
+	maxDynamicStorageBuffersPerPipelineLayout: u32,
+	maxSampledTexturesPerShaderStage: u32,
+	maxSamplersPerShaderStage: u32,
+	maxStorageBuffersPerShaderStage: u32,
+	maxStorageTexturesPerShaderStage: u32,
+	maxUniformBuffersPerShaderStage: u32,
+	maxUniformBufferBindingSize: u64,
+	maxStorageBufferBindingSize: u64,
+	minUniformBufferOffsetAlignment: u32,
+	minStorageBufferOffsetAlignment: u32,
+	maxVertexBuffers: u32,
+	maxBufferSize: u64,
+	maxVertexAttributes: u32,
+	maxVertexBufferArrayStride: u32,
+	maxInterStageShaderComponents: u32,
+	maxInterStageShaderVariables: u32,
+	maxColorAttachments: u32,
+	maxColorAttachmentBytesPerSample: u32,
+	maxComputeWorkgroupStorageSize: u32,
+	maxComputeInvocationsPerWorkgroup: u32,
+	maxComputeWorkgroupSizeX: u32,
+	maxComputeWorkgroupSizeY: u32,
+	maxComputeWorkgroupSizeZ: u32,
+	maxComputeWorkgroupsPerDimension: u32,
+}
+
+MultisampleState :: struct {
+	nextInChain: ^ChainedStruct,
+	count: u32,
+	mask: u32,
+	alphaToCoverageEnabled: b32,
+}
+
+Origin3D :: struct {
+	x: u32,
+	y: u32,
+	z: u32,
+}
+
+PipelineLayoutDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	bindGroupLayoutCount: uint,
+	bindGroupLayouts: [^]BindGroupLayout `fmt:"v,bindGroupLayoutCount"`,
+}
+
+PrimitiveDepthClipControl :: struct {
+	using chain: ChainedStruct,
+	unclippedDepth: b32,
+}
+
+PrimitiveState :: struct {
+	nextInChain: ^ChainedStruct,
+	topology: PrimitiveTopology,
+	stripIndexFormat: IndexFormat,
+	frontFace: FrontFace,
+	cullMode: CullMode,
+}
+
+QuerySetDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	type: QueryType,
+	count: u32,
+}
+
+QueueDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+}
+
+RenderBundleDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+}
+
+RenderBundleEncoderDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	colorFormatCount: uint,
+	colorFormats: [^]TextureFormat `fmt:"v,colorFormatCount"`,
+	depthStencilFormat: TextureFormat,
+	sampleCount: u32,
+	depthReadOnly: b32,
+	stencilReadOnly: b32,
+}
+
+RenderPassDepthStencilAttachment :: struct {
+	view: TextureView,
+	depthLoadOp: LoadOp,
+	depthStoreOp: StoreOp,
+	depthClearValue: f32,
+	depthReadOnly: b32,
+	stencilLoadOp: LoadOp,
+	stencilStoreOp: StoreOp,
+	stencilClearValue: u32,
+	stencilReadOnly: b32,
+}
+
+RenderPassDescriptorMaxDrawCount :: struct {
+	using chain: ChainedStruct,
+	maxDrawCount: u64,
+}
+
+RenderPassTimestampWrites :: struct {
+	querySet: QuerySet,
+	beginningOfPassWriteIndex: u32,
+	endOfPassWriteIndex: u32,
+}
+
+RequestAdapterOptions :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ compatibleSurface: Surface,
+	powerPreference: PowerPreference,
+	backendType: BackendType,
+	forceFallbackAdapter: b32,
+}
+
+SamplerBindingLayout :: struct {
+	nextInChain: ^ChainedStruct,
+	type: SamplerBindingType,
+}
+
+SamplerDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	addressModeU: AddressMode,
+	addressModeV: AddressMode,
+	addressModeW: AddressMode,
+	magFilter: FilterMode,
+	minFilter: FilterMode,
+	mipmapFilter: MipmapFilterMode,
+	lodMinClamp: f32,
+	lodMaxClamp: f32,
+	compare: CompareFunction,
+	maxAnisotropy: u16,
+}
+
+ShaderModuleCompilationHint :: struct {
+	nextInChain: ^ChainedStruct,
+	entryPoint: cstring,
+	layout: PipelineLayout,
+}
+
+ShaderModuleSPIRVDescriptor :: struct {
+	using chain: ChainedStruct,
+	codeSize: u32,
+	code: /* const */ [^]u32 `fmt:"v,codeSize"`,
+}
+
+ShaderModuleWGSLDescriptor :: struct {
+	using chain: ChainedStruct,
+	code: cstring,
+}
+
+StencilFaceState :: struct {
+	compare: CompareFunction,
+	failOp: StencilOperation,
+	depthFailOp: StencilOperation,
+	passOp: StencilOperation,
+}
+
+StorageTextureBindingLayout :: struct {
+	nextInChain: ^ChainedStruct,
+	access: StorageTextureAccess,
+	format: TextureFormat,
+	viewDimension: TextureViewDimension,
+}
+
+SurfaceCapabilities :: struct {
+	nextInChain: ^ChainedStructOut,
+	formatCount: uint,
+	formats: /* const */ [^]TextureFormat `fmt:"v,formatCount"`,
+	presentModeCount: uint,
+	presentModes: /* const */ [^]PresentMode `fmt:"v,presentModeCount"`,
+	alphaModeCount: uint,
+	alphaModes: /* const */ [^]CompositeAlphaMode `fmt:"v,alphaModeCount"`,
+}
+
+SurfaceConfiguration :: struct {
+	nextInChain: ^ChainedStruct,
+	device: Device,
+	format: TextureFormat,
+	usage: TextureUsageFlags,
+	viewFormatCount: uint,
+	viewFormats: [^]TextureFormat `fmt:"v,viewFormatCount"`,
+	alphaMode: CompositeAlphaMode,
+	width: u32,
+	height: u32,
+	presentMode: PresentMode,
+}
+
+SurfaceDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+}
+
+SurfaceDescriptorFromAndroidNativeWindow :: struct {
+	using chain: ChainedStruct,
+	window: rawptr,
+}
+
+SurfaceDescriptorFromCanvasHTMLSelector :: struct {
+	using chain: ChainedStruct,
+	selector: cstring,
+}
+
+SurfaceDescriptorFromMetalLayer :: struct {
+	using chain: ChainedStruct,
+	layer: rawptr,
+}
+
+SurfaceDescriptorFromWaylandSurface :: struct {
+	using chain: ChainedStruct,
+	display: rawptr,
+	surface: rawptr,
+}
+
+SurfaceDescriptorFromWindowsHWND :: struct {
+	using chain: ChainedStruct,
+	hinstance: rawptr,
+	hwnd: rawptr,
+}
+
+SurfaceDescriptorFromXcbWindow :: struct {
+	using chain: ChainedStruct,
+	connection: rawptr,
+	window: u32,
+}
+
+SurfaceDescriptorFromXlibWindow :: struct {
+	using chain: ChainedStruct,
+	display: rawptr,
+	window: u64,
+}
+
+SurfaceTexture :: struct {
+	texture: Texture,
+	suboptimal: b32,
+	status: SurfaceGetCurrentTextureStatus,
+}
+
+TextureBindingLayout :: struct {
+	nextInChain: ^ChainedStruct,
+	sampleType: TextureSampleType,
+	viewDimension: TextureViewDimension,
+	multisampled: b32,
+}
+
+TextureDataLayout :: struct {
+	nextInChain: ^ChainedStruct,
+	offset: u64,
+	bytesPerRow: u32,
+	rowsPerImage: u32,
+}
+
+TextureViewDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	format: TextureFormat,
+	dimension: TextureViewDimension,
+	baseMipLevel: u32,
+	mipLevelCount: u32,
+	baseArrayLayer: u32,
+	arrayLayerCount: u32,
+	aspect: TextureAspect,
+}
+
+VertexAttribute :: struct {
+	format: VertexFormat,
+	offset: u64,
+	shaderLocation: u32,
+}
+
+BindGroupDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	layout: BindGroupLayout,
+	entryCount: uint,
+	entries: [^]BindGroupEntry `fmt:"v,entryCount"`,
+}
+
+BindGroupLayoutEntry :: struct {
+	nextInChain: ^ChainedStruct,
+	binding: u32,
+	visibility: ShaderStageFlags,
+	buffer: BufferBindingLayout,
+	sampler: SamplerBindingLayout,
+	texture: TextureBindingLayout,
+	storageTexture: StorageTextureBindingLayout,
+}
+
+BlendState :: struct {
+	color: BlendComponent,
+	alpha: BlendComponent,
+}
+
+CompilationInfo :: struct {
+	nextInChain: ^ChainedStruct,
+	messageCount: uint,
+	messages: [^]CompilationMessage `fmt:"v,messageCount"`,
+}
+
+ComputePassDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	/* NULLABLE */ timestampWrites: /* const */ ^ComputePassTimestampWrites,
+}
+
+DepthStencilState :: struct {
+	nextInChain: ^ChainedStruct,
+	format: TextureFormat,
+	depthWriteEnabled: b32,
+	depthCompare: CompareFunction,
+	stencilFront: StencilFaceState,
+	stencilBack: StencilFaceState,
+	stencilReadMask: u32,
+	stencilWriteMask: u32,
+	depthBias: i32,
+	depthBiasSlopeScale: f32,
+	depthBiasClamp: f32,
+}
+
+ImageCopyBuffer :: struct {
+	nextInChain: ^ChainedStruct,
+	layout: TextureDataLayout,
+	buffer: Buffer,
+}
+
+ImageCopyTexture :: struct {
+	nextInChain: ^ChainedStruct,
+	texture: Texture,
+	mipLevel: u32,
+	origin: Origin3D,
+	aspect: TextureAspect,
+}
+
+ProgrammableStageDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	module: ShaderModule,
+	/* NULLABLE */ entryPoint: cstring,
+	constantCount: uint,
+	constants: [^]ConstantEntry `fmt:"v,constantCount"`,
+}
+
+RenderPassColorAttachment :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ view: TextureView,
+	// depthSlice: u32,
+	/* NULLABLE */ resolveTarget: TextureView,
+	loadOp: LoadOp,
+	storeOp: StoreOp,
+	clearValue: Color,
+}
+
+RequiredLimits :: struct {
+	nextInChain: ^ChainedStruct,
+	limits: Limits,
+}
+
+ShaderModuleDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	hintCount: uint,
+	hints: [^]ShaderModuleCompilationHint `fmt:"v,hintCount"`,
+}
+
+SupportedLimits :: struct {
+	nextInChain: ^ChainedStructOut,
+	limits: Limits,
+}
+
+TextureDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	usage: TextureUsageFlags,
+	dimension: TextureDimension,
+	size: Extent3D,
+	format: TextureFormat,
+	mipLevelCount: u32,
+	sampleCount: u32,
+	viewFormatCount: uint,
+	viewFormats: [^]TextureFormat `fmt:"v,viewFormatCount"`,
+}
+
+VertexBufferLayout :: struct {
+	arrayStride: u64,
+	stepMode: VertexStepMode,
+	attributeCount: uint,
+	attributes: [^]VertexAttribute `fmt:"v,attributeCount"`,
+}
+
+BindGroupLayoutDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	entryCount: uint,
+	entries: [^]BindGroupLayoutEntry `fmt:"v,entryCount"`,
+}
+
+ColorTargetState :: struct {
+	nextInChain: ^ChainedStruct,
+	format: TextureFormat,
+	/* NULLABLE */ blend: /* const */ ^BlendState,
+	writeMask: ColorWriteMaskFlags,
+}
+
+ComputePipelineDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	/* NULLABLE */ layout: PipelineLayout,
+	compute: ProgrammableStageDescriptor,
+}
+
+DeviceDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	requiredFeatureCount: uint,
+	requiredFeatures: [^]FeatureName `fmt:"v,requiredFeatureCount"`,
+	/* NULLABLE */ requiredLimits: /* const */ ^RequiredLimits,
+	defaultQueue: QueueDescriptor,
+	deviceLostCallback: DeviceLostCallback,
+	deviceLostUserdata: rawptr,
+}
+
+RenderPassDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	colorAttachmentCount: uint,
+	colorAttachments: [^]RenderPassColorAttachment `fmt:"v,colorAttachmentCount"`,
+	/* NULLABLE */ depthStencilAttachment: /* const */ ^RenderPassDepthStencilAttachment,
+	/* NULLABLE */ occlusionQuerySet: QuerySet,
+	/* NULLABLE */ timestampWrites: /* const */ ^RenderPassTimestampWrites,
+}
+
+VertexState :: struct {
+	nextInChain: ^ChainedStruct,
+	module: ShaderModule,
+	/* NULLABLE */ entryPoint: cstring,
+	constantCount: uint,
+	constants: [^]ConstantEntry `fmt:"v,constantCount"`,
+	bufferCount: uint,
+	buffers: [^]VertexBufferLayout `fmt:"v,bufferCount"`,
+}
+
+FragmentState :: struct {
+	nextInChain: ^ChainedStruct,
+	module: ShaderModule,
+	/* NULLABLE */ entryPoint: cstring,
+	constantCount: uint,
+	constants: [^]ConstantEntry `fmt:"v,constantCount"`,
+	targetCount: uint,
+	targets: [^]ColorTargetState `fmt:"v,targetCount"`,
+}
+
+RenderPipelineDescriptor :: struct {
+	nextInChain: ^ChainedStruct,
+	/* NULLABLE */ label: cstring,
+	/* NULLABLE */ layout: PipelineLayout,
+	vertex: VertexState,
+	primitive: PrimitiveState,
+	/* NULLABLE */ depthStencil: /* const */ ^DepthStencilState,
+	multisample: MultisampleState,
+	/* NULLABLE */ fragment: /* const */ ^FragmentState,
+}
+
+@(link_prefix="wgpu", default_calling_convention="c")
+foreign libwgpu {
+	CreateInstance :: proc(/* NULLABLE */ descriptor: /* const */ ^InstanceDescriptor = nil) -> Instance ---
+	GetProcAddress :: proc(device: Device, procName: cstring) -> Proc ---
+
+	// Methods of Adapter
+	@(link_name="wgpuAdapterEnumerateFeatures")
+	RawAdapterEnumerateFeatures :: proc(adapter: Adapter, features: [^]FeatureName) -> uint ---
+	@(link_name="wgpuAdapterGetLimits")
+	RawAdapterGetLimits :: proc(adapter: Adapter, limits: ^SupportedLimits) -> b32 ---
+	@(link_name="wgpuAdapterGetProperties")
+	RawAdapterGetProperties :: proc(adapter: Adapter, properties: ^AdapterProperties) ---
+	AdapterHasFeature :: proc(adapter: Adapter, feature: FeatureName) -> b32 ---
+	// AdapterRequestAdapterInfo :: proc(adapter: Adapter, callback: AdapterRequestAdapterInfoCallback, /* NULLABLE */ userdata: rawptr) ---
+	AdapterRequestDevice :: proc(adapter: Adapter, /* NULLABLE */ descriptor: /* const */ ^DeviceDescriptor, callback: AdapterRequestDeviceCallback, /* NULLABLE */ userdata: rawptr = nil) ---
+	AdapterReference :: proc(adapter: Adapter) ---
+	AdapterRelease :: proc(adapter: Adapter) ---
+
+	// Methods of BindGroup
+	BindGroupSetLabel :: proc(bindGroup: BindGroup, label: cstring) ---
+	BindGroupReference :: proc(bindGroup: BindGroup) ---
+	BindGroupRelease :: proc(bindGroup: BindGroup) ---
+
+	// Methods of BindGroupLayout
+	BindGroupLayoutSetLabel :: proc(bindGroupLayout: BindGroupLayout, label: cstring) ---
+	BindGroupLayoutReference :: proc(bindGroupLayout: BindGroupLayout) ---
+	BindGroupLayoutRelease :: proc(bindGroupLayout: BindGroupLayout) ---
+
+	// Methods of Buffer
+	BufferDestroy :: proc(buffer: Buffer) ---
+	@(link_name="wgpuBufferGetConstMappedRange")
+	RawBufferGetConstMappedRange :: proc(buffer: Buffer, offset: uint, size: uint) -> /* const */ rawptr ---
+	BufferGetMapState :: proc(buffer: Buffer) -> BufferMapState ---
+	@(link_name="wgpuBufferGetMappedRange")
+	RawBufferGetMappedRange :: proc(buffer: Buffer, offset: uint, size: uint) -> rawptr ---
+	BufferGetSize :: proc(buffer: Buffer) -> u64 ---
+	BufferGetUsage :: proc(buffer: Buffer) -> BufferUsageFlags ---
+	BufferMapAsync :: proc(buffer: Buffer, mode: MapModeFlags, offset: uint, size: uint, callback: BufferMapAsyncCallback, /* NULLABLE */ userdata: rawptr = nil) ---
+	BufferSetLabel :: proc(buffer: Buffer, label: cstring) ---
+	BufferUnmap :: proc(buffer: Buffer) ---
+	BufferReference :: proc(buffer: Buffer) ---
+	BufferRelease :: proc(buffer: Buffer) ---
+
+	// Methods of CommandBuffer
+	CommandBufferSetLabel :: proc(commandBuffer: CommandBuffer, label: cstring) ---
+	CommandBufferReference :: proc(commandBuffer: CommandBuffer) ---
+	CommandBufferRelease :: proc(commandBuffer: CommandBuffer) ---
+
+	// Methods of CommandEncoder
+	CommandEncoderBeginComputePass :: proc(commandEncoder: CommandEncoder, /* NULLABLE */ descriptor: /* const */ ^ComputePassDescriptor = nil) -> ComputePassEncoder ---
+	CommandEncoderBeginRenderPass :: proc(commandEncoder: CommandEncoder, descriptor: /* const */ ^RenderPassDescriptor) -> RenderPassEncoder ---
+	CommandEncoderClearBuffer :: proc(commandEncoder: CommandEncoder, buffer: Buffer, offset: u64, size: u64) ---
+	CommandEncoderCopyBufferToBuffer :: proc(commandEncoder: CommandEncoder, source: Buffer, sourceOffset: u64, destination: Buffer, destinationOffset: u64, size: u64) ---
+	CommandEncoderCopyBufferToTexture :: proc(commandEncoder: CommandEncoder, source: /* const */ ^ImageCopyBuffer, destination: /* const */ ^ImageCopyTexture, copySize: /* const */ ^Extent3D) ---
+	CommandEncoderCopyTextureToBuffer :: proc(commandEncoder: CommandEncoder, source: /* const */ ^ImageCopyTexture, destination: /* const */ ^ImageCopyBuffer, copySize: /* const */ ^Extent3D) ---
+	CommandEncoderCopyTextureToTexture :: proc(commandEncoder: CommandEncoder, source: /* const */ ^ImageCopyTexture, destination: /* const */ ^ImageCopyTexture, copySize: /* const */ ^Extent3D) ---
+	CommandEncoderFinish :: proc(commandEncoder: CommandEncoder, /* NULLABLE */ descriptor: /* const */ ^CommandBufferDescriptor = nil) -> CommandBuffer ---
+	CommandEncoderInsertDebugMarker :: proc(commandEncoder: CommandEncoder, markerLabel: cstring) ---
+	CommandEncoderPopDebugGroup :: proc(commandEncoder: CommandEncoder) ---
+	CommandEncoderPushDebugGroup :: proc(commandEncoder: CommandEncoder, groupLabel: cstring) ---
+	CommandEncoderResolveQuerySet :: proc(commandEncoder: CommandEncoder, querySet: QuerySet, firstQuery: u32, queryCount: u32, destination: Buffer, destinationOffset: u64) ---
+	CommandEncoderSetLabel :: proc(commandEncoder: CommandEncoder, label: cstring) ---
+	CommandEncoderWriteTimestamp :: proc(commandEncoder: CommandEncoder, querySet: QuerySet, queryIndex: u32) ---
+	CommandEncoderReference :: proc(commandEncoder: CommandEncoder) ---
+	CommandEncoderRelease :: proc(commandEncoder: CommandEncoder) ---
+
+	// Methods of ComputePassEncoder
+	ComputePassEncoderDispatchWorkgroups :: proc(computePassEncoder: ComputePassEncoder, workgroupCountX: u32, workgroupCountY: u32, workgroupCountZ: u32) ---
+	ComputePassEncoderDispatchWorkgroupsIndirect :: proc(computePassEncoder: ComputePassEncoder, indirectBuffer: Buffer, indirectOffset: u64) ---
+	ComputePassEncoderEnd :: proc(computePassEncoder: ComputePassEncoder) ---
+	ComputePassEncoderInsertDebugMarker :: proc(computePassEncoder: ComputePassEncoder, markerLabel: cstring) ---
+	ComputePassEncoderPopDebugGroup :: proc(computePassEncoder: ComputePassEncoder) ---
+	ComputePassEncoderPushDebugGroup :: proc(computePassEncoder: ComputePassEncoder, groupLabel: cstring) ---
+	@(link_name="wgpuComputePassEncoderSetBindGroup")
+	RawComputePassEncoderSetBindGroup :: proc(computePassEncoder: ComputePassEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsetCount: uint, dynamicOffsets: [^]u32) ---
+	ComputePassEncoderSetLabel :: proc(computePassEncoder: ComputePassEncoder, label: cstring) ---
+	ComputePassEncoderSetPipeline :: proc(computePassEncoder: ComputePassEncoder, pipeline: ComputePipeline) ---
+	ComputePassEncoderReference :: proc(computePassEncoder: ComputePassEncoder) ---
+	ComputePassEncoderRelease :: proc(computePassEncoder: ComputePassEncoder) ---
+
+	// Methods of ComputePipeline
+	ComputePipelineGetBindGroupLayout :: proc(computePipeline: ComputePipeline, groupIndex: u32) -> BindGroupLayout ---
+	ComputePipelineSetLabel :: proc(computePipeline: ComputePipeline, label: cstring) ---
+	ComputePipelineReference :: proc(computePipeline: ComputePipeline) ---
+	ComputePipelineRelease :: proc(computePipeline: ComputePipeline) ---
+
+	// Methods of Device
+	DeviceCreateBindGroup :: proc(device: Device, descriptor: /* const */ ^BindGroupDescriptor) -> BindGroup ---
+	DeviceCreateBindGroupLayout :: proc(device: Device, descriptor: /* const */ ^BindGroupLayoutDescriptor) -> BindGroupLayout ---
+	DeviceCreateBuffer :: proc(device: Device, descriptor: /* const */ ^BufferDescriptor) -> Buffer ---
+	DeviceCreateCommandEncoder :: proc(device: Device, /* NULLABLE */ descriptor: /* const */ ^CommandEncoderDescriptor = nil) -> CommandEncoder ---
+	DeviceCreateComputePipeline :: proc(device: Device, descriptor: /* const */ ^ComputePipelineDescriptor) -> ComputePipeline ---
+	DeviceCreateComputePipelineAsync :: proc(device: Device, descriptor: /* const */ ^ComputePipelineDescriptor, callback: DeviceCreateComputePipelineAsyncCallback, /* NULLABLE */ userdata: rawptr = nil) ---
+	DeviceCreatePipelineLayout :: proc(device: Device, descriptor: /* const */ ^PipelineLayoutDescriptor) -> PipelineLayout ---
+	DeviceCreateQuerySet :: proc(device: Device, descriptor: /* const */ ^QuerySetDescriptor) -> QuerySet ---
+	DeviceCreateRenderBundleEncoder :: proc(device: Device, descriptor: /* const */ ^RenderBundleEncoderDescriptor) -> RenderBundleEncoder ---
+	DeviceCreateRenderPipeline :: proc(device: Device, descriptor: /* const */ ^RenderPipelineDescriptor) -> RenderPipeline ---
+	DeviceCreateRenderPipelineAsync :: proc(device: Device, descriptor: /* const */ ^RenderPipelineDescriptor, callback: DeviceCreateRenderPipelineAsyncCallback, /* NULLABLE */ userdata: rawptr = nil) ---
+	DeviceCreateSampler :: proc(device: Device, /* NULLABLE */ descriptor: /* const */ ^SamplerDescriptor = nil) -> Sampler ---
+	DeviceCreateShaderModule :: proc(device: Device, descriptor: /* const */ ^ShaderModuleDescriptor) -> ShaderModule ---
+	DeviceCreateTexture :: proc(device: Device, descriptor: /* const */ ^TextureDescriptor) -> Texture ---
+	DeviceDestroy :: proc(device: Device) ---
+	@(link_name="wgpuDeviceEnumerateFeatures")
+	RawDeviceEnumerateFeatures :: proc(device: Device, features: ^FeatureName) -> uint ---
+	@(link_name="wgpuDeviceGetLimits")
+	RawDeviceGetLimits :: proc(device: Device, limits: ^SupportedLimits) -> b32 ---
+	DeviceGetQueue :: proc(device: Device) -> Queue ---
+	DeviceHasFeature :: proc(device: Device, feature: FeatureName) -> b32 ---
+	DevicePopErrorScope :: proc(device: Device, callback: ErrorCallback, userdata: rawptr) ---
+	DevicePushErrorScope :: proc(device: Device, filter: ErrorFilter) ---
+	DeviceSetLabel :: proc(device: Device, label: cstring) ---
+	DeviceSetUncapturedErrorCallback :: proc(device: Device, callback: ErrorCallback, userdata: rawptr) ---
+	DeviceReference :: proc(device: Device) ---
+	DeviceRelease :: proc(device: Device) ---
+
+	// Methods of Instance
+	InstanceCreateSurface :: proc(instance: Instance, descriptor: /* const */ ^SurfaceDescriptor) -> Surface ---
+	// InstanceHasWGSLLanguageFeature :: proc(instance: Instance, feature: WGSLFeatureName) -> b32 ---
+	InstanceProcessEvents :: proc(instance: Instance) ---
+	InstanceRequestAdapter :: proc(instance: Instance, /* NULLABLE */ options: /* const */ ^RequestAdapterOptions, callback: InstanceRequestAdapterCallback, /* NULLABLE */ userdata: rawptr = nil) ---
+	InstanceReference :: proc(instance: Instance) ---
+	InstanceRelease :: proc(instance: Instance) ---
+
+	// Methods of PipelineLayout
+	PipelineLayoutSetLabel :: proc(pipelineLayout: PipelineLayout, label: cstring) ---
+	PipelineLayoutReference :: proc(pipelineLayout: PipelineLayout) ---
+	PipelineLayoutRelease :: proc(pipelineLayout: PipelineLayout) ---
+
+	// Methods of QuerySet
+	QuerySetDestroy :: proc(querySet: QuerySet) ---
+	QuerySetGetCount :: proc(querySet: QuerySet) -> u32 ---
+	QuerySetGetType :: proc(querySet: QuerySet) -> QueryType ---
+	QuerySetSetLabel :: proc(querySet: QuerySet, label: cstring) ---
+	QuerySetReference :: proc(querySet: QuerySet) ---
+	QuerySetRelease :: proc(querySet: QuerySet) ---
+
+	// Methods of Queue
+	QueueOnSubmittedWorkDone :: proc(queue: Queue, callback: QueueOnSubmittedWorkDoneCallback, /* NULLABLE */ userdata: rawptr = nil) ---
+	QueueSetLabel :: proc(queue: Queue, label: cstring) ---
+	@(link_name="wgpuQueueSubmit")
+	RawQueueSubmit :: proc(queue: Queue, commandCount: uint, commands: [^]CommandBuffer) ---
+	QueueWriteBuffer :: proc(queue: Queue, buffer: Buffer, bufferOffset: u64, data: /* const */ rawptr, size: uint) ---
+	QueueWriteTexture :: proc(queue: Queue, destination: /* const */ ^ImageCopyTexture, data: /* const */ rawptr, dataSize: uint, dataLayout: /* const */ ^TextureDataLayout, writeSize: /* const */ ^Extent3D) ---
+	QueueReference :: proc(queue: Queue) ---
+	QueueRelease :: proc(queue: Queue) ---
+
+	// Methods of RenderBundle
+	RenderBundleSetLabel :: proc(renderBundle: RenderBundle, label: cstring) ---
+	RenderBundleReference :: proc(renderBundle: RenderBundle) ---
+	RenderBundleRelease :: proc(renderBundle: RenderBundle) ---
+
+	// Methods of RenderBundleEncoder
+	RenderBundleEncoderDraw :: proc(renderBundleEncoder: RenderBundleEncoder, vertexCount: u32, instanceCount: u32, firstVertex: u32, firstInstance: u32) ---
+	RenderBundleEncoderDrawIndexed :: proc(renderBundleEncoder: RenderBundleEncoder, indexCount: u32, instanceCount: u32, firstIndex: u32, baseVertex: i32, firstInstance: u32) ---
+	RenderBundleEncoderDrawIndexedIndirect :: proc(renderBundleEncoder: RenderBundleEncoder, indirectBuffer: Buffer, indirectOffset: u64) ---
+	RenderBundleEncoderDrawIndirect :: proc(renderBundleEncoder: RenderBundleEncoder, indirectBuffer: Buffer, indirectOffset: u64) ---
+	RenderBundleEncoderFinish :: proc(renderBundleEncoder: RenderBundleEncoder, /* NULLABLE */ descriptor: /* const */ ^RenderBundleDescriptor = nil) -> RenderBundle ---
+	RenderBundleEncoderInsertDebugMarker :: proc(renderBundleEncoder: RenderBundleEncoder, markerLabel: cstring) ---
+	RenderBundleEncoderPopDebugGroup :: proc(renderBundleEncoder: RenderBundleEncoder) ---
+	RenderBundleEncoderPushDebugGroup :: proc(renderBundleEncoder: RenderBundleEncoder, groupLabel: cstring) ---
+	@(link_name="wgpuRenderBundleEncoderSetBindGroup")
+	RawRenderBundleEncoderSetBindGroup :: proc(renderBundleEncoder: RenderBundleEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsetCount: uint, dynamicOffsets: [^]u32) ---
+	RenderBundleEncoderSetIndexBuffer :: proc(renderBundleEncoder: RenderBundleEncoder, buffer: Buffer, format: IndexFormat, offset: u64, size: u64) ---
+	RenderBundleEncoderSetLabel :: proc(renderBundleEncoder: RenderBundleEncoder, label: cstring) ---
+	RenderBundleEncoderSetPipeline :: proc(renderBundleEncoder: RenderBundleEncoder, pipeline: RenderPipeline) ---
+	RenderBundleEncoderSetVertexBuffer :: proc(renderBundleEncoder: RenderBundleEncoder, slot: u32, /* NULLABLE */ buffer: Buffer, offset: u64, size: u64) ---
+	RenderBundleEncoderReference :: proc(renderBundleEncoder: RenderBundleEncoder) ---
+	RenderBundleEncoderRelease :: proc(renderBundleEncoder: RenderBundleEncoder) ---
+
+	// Methods of RenderPassEncoder
+	RenderPassEncoderBeginOcclusionQuery :: proc(renderPassEncoder: RenderPassEncoder, queryIndex: u32) ---
+	RenderPassEncoderDraw :: proc(renderPassEncoder: RenderPassEncoder, vertexCount: u32, instanceCount: u32, firstVertex: u32, firstInstance: u32) ---
+	RenderPassEncoderDrawIndexed :: proc(renderPassEncoder: RenderPassEncoder, indexCount: u32, instanceCount: u32, firstIndex: u32, baseVertex: i32, firstInstance: u32) ---
+	RenderPassEncoderDrawIndexedIndirect :: proc(renderPassEncoder: RenderPassEncoder, indirectBuffer: Buffer, indirectOffset: u64) ---
+	RenderPassEncoderDrawIndirect :: proc(renderPassEncoder: RenderPassEncoder, indirectBuffer: Buffer, indirectOffset: u64) ---
+	RenderPassEncoderEnd :: proc(renderPassEncoder: RenderPassEncoder) ---
+	RenderPassEncoderEndOcclusionQuery :: proc(renderPassEncoder: RenderPassEncoder) ---
+	@(link_name="wgpuRenderPassEncoderExecuteBundles")
+	RawRenderPassEncoderExecuteBundles :: proc(renderPassEncoder: RenderPassEncoder, bundleCount: uint, bundles: [^]RenderBundle) ---
+	RenderPassEncoderInsertDebugMarker :: proc(renderPassEncoder: RenderPassEncoder, markerLabel: cstring) ---
+	RenderPassEncoderPopDebugGroup :: proc(renderPassEncoder: RenderPassEncoder) ---
+	RenderPassEncoderPushDebugGroup :: proc(renderPassEncoder: RenderPassEncoder, groupLabel: cstring) ---
+	@(link_name="wgpuRenderPassEncoderSetBindGroup")
+	RawRenderPassEncoderSetBindGroup :: proc(renderPassEncoder: RenderPassEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsetCount: uint, dynamicOffsets: [^]u32) ---
+	RenderPassEncoderSetBlendConstant :: proc(renderPassEncoder: RenderPassEncoder, color: /* const */ ^Color) ---
+	RenderPassEncoderSetIndexBuffer :: proc(renderPassEncoder: RenderPassEncoder, buffer: Buffer, format: IndexFormat, offset: u64, size: u64) ---
+	RenderPassEncoderSetLabel :: proc(renderPassEncoder: RenderPassEncoder, label: cstring) ---
+	RenderPassEncoderSetPipeline :: proc(renderPassEncoder: RenderPassEncoder, pipeline: RenderPipeline) ---
+	RenderPassEncoderSetScissorRect :: proc(renderPassEncoder: RenderPassEncoder, x: u32, y: u32, width: u32, height: u32) ---
+	RenderPassEncoderSetStencilReference :: proc(renderPassEncoder: RenderPassEncoder, reference: u32) ---
+	RenderPassEncoderSetVertexBuffer :: proc(renderPassEncoder: RenderPassEncoder, slot: u32, /* NULLABLE */ buffer: Buffer, offset: u64, size: u64) ---
+	RenderPassEncoderSetViewport :: proc(renderPassEncoder: RenderPassEncoder, x: f32, y: f32, width: f32, height: f32, minDepth: f32, maxDepth: f32) ---
+	RenderPassEncoderReference :: proc(renderPassEncoder: RenderPassEncoder) ---
+	RenderPassEncoderRelease :: proc(renderPassEncoder: RenderPassEncoder) ---
+
+	// Methods of RenderPipeline
+	RenderPipelineGetBindGroupLayout :: proc(renderPipeline: RenderPipeline, groupIndex: u32) -> BindGroupLayout ---
+	RenderPipelineSetLabel :: proc(renderPipeline: RenderPipeline, label: cstring) ---
+	RenderPipelineReference :: proc(renderPipeline: RenderPipeline) ---
+	RenderPipelineRelease :: proc(renderPipeline: RenderPipeline) ---
+
+	// Methods of Sampler
+	SamplerSetLabel :: proc(sampler: Sampler, label: cstring) ---
+	SamplerReference :: proc(sampler: Sampler) ---
+	SamplerRelease :: proc(sampler: Sampler) ---
+
+	// Methods of ShaderModule
+	ShaderModuleGetCompilationInfo :: proc(shaderModule: ShaderModule, callback: ShaderModuleGetCompilationInfoCallback, /* NULLABLE */ userdata: rawptr = nil) ---
+	ShaderModuleSetLabel :: proc(shaderModule: ShaderModule, label: cstring) ---
+	ShaderModuleReference :: proc(shaderModule: ShaderModule) ---
+	ShaderModuleRelease :: proc(shaderModule: ShaderModule) ---
+
+	// Methods of Surface
+	SurfaceConfigure :: proc(surface: Surface, config: /* const */ ^SurfaceConfiguration) ---
+	@(link_name="wgpuSurfaceGetCapabilities")
+	RawSurfaceGetCapabilities :: proc(surface: Surface, adapter: Adapter, capabilities: ^SurfaceCapabilities) ---
+	@(link_name="wgpuSurfaceGetCurrentTexture")
+	RawSurfaceGetCurrentTexture :: proc(surface: Surface, surfaceTexture: ^SurfaceTexture) ---
+	SurfaceGetPreferredFormat :: proc(surface: Surface, adapter: Adapter) -> TextureFormat ---
+	SurfacePresent :: proc(surface: Surface) ---
+	// SurfaceSetLabel :: proc(surface: Surface, label: cstring) ---
+	SurfaceUnconfigure :: proc(surface: Surface) ---
+	SurfaceReference :: proc(surface: Surface) ---
+	SurfaceRelease :: proc(surface: Surface) ---
+
+	// Methods of SurfaceCapabilities
+	SurfaceCapabilitiesFreeMembers :: proc(surfaceCapabilities: SurfaceCapabilities) ---
+
+	// Methods of Texture
+	TextureCreateView :: proc(texture: Texture, /* NULLABLE */ descriptor: /* const */ ^TextureViewDescriptor = nil) -> TextureView ---
+	TextureDestroy :: proc(texture: Texture) ---
+	TextureGetDepthOrArrayLayers :: proc(texture: Texture) -> u32 ---
+	TextureGetDimension :: proc(texture: Texture) -> TextureDimension ---
+	TextureGetFormat :: proc(texture: Texture) -> TextureFormat ---
+	TextureGetHeight :: proc(texture: Texture) -> u32 ---
+	TextureGetMipLevelCount :: proc(texture: Texture) -> u32 ---
+	TextureGetSampleCount :: proc(texture: Texture) -> u32 ---
+	TextureGetUsage :: proc(texture: Texture) -> TextureUsageFlags ---
+	TextureGetWidth :: proc(texture: Texture) -> u32 ---
+	TextureSetLabel :: proc(texture: Texture, label: cstring) ---
+	TextureReference :: proc(texture: Texture) ---
+	TextureRelease :: proc(texture: Texture) ---
+
+	// Methods of TextureView
+	TextureViewSetLabel :: proc(textureView: TextureView, label: cstring) ---
+	TextureViewReference :: proc(textureView: TextureView) ---
+	TextureViewRelease :: proc(textureView: TextureView) ---
+}
+
+// Wrappers of Adapter
+
+AdapterEnumerateFeatures :: proc(adapter: Adapter, allocator := context.allocator) -> []FeatureName {
+	count := RawAdapterEnumerateFeatures(adapter, nil)
+	features := make([]FeatureName, count, allocator)
+	RawAdapterEnumerateFeatures(adapter, raw_data(features))
+	return features
+}
+
+AdapterGetLimits :: proc(adapter: Adapter) -> (limits: SupportedLimits, ok: bool) {
+	ok = bool(RawAdapterGetLimits(adapter, &limits))
+	return
+}
+
+AdapterGetProperties :: proc(adapter: Adapter) -> (properties: AdapterProperties) {
+	RawAdapterGetProperties(adapter, &properties)
+	return
+}
+
+// Wrappers of Buffer
+
+BufferGetConstMappedRange :: proc(buffer: Buffer, offset: uint, size: uint) -> []byte {
+	return ([^]byte)(RawBufferGetConstMappedRange(buffer, offset, size))[:size]
+}
+
+BufferGetConstMappedRangeTyped :: proc(buffer: Buffer, offset: uint, $T: typeid) -> ^T
+	where !intrinsics.type_is_sliceable(T) {
+
+	return (^T)(RawBufferGetConstMappedRange(buffer, 0, size_of(T)))
+}
+
+BufferGetConstMappedRangeSlice :: proc(buffer: Buffer, offset: uint, length: uint, $T: typeid) -> []T {
+	return ([^]T)(RawBufferGetConstMappedRange(buffer, offset, size_of(T)*length))[:length]
+}
+
+BufferGetMappedRange :: proc(buffer: Buffer, offset: uint, size: uint) -> []byte {
+	return ([^]byte)(RawBufferGetMappedRange(buffer, offset, size))[:size]
+}
+
+BufferGetMappedRangeTyped :: proc(buffer: Buffer, offset: uint, $T: typeid) -> ^T
+	where !intrinsics.type_is_sliceable(T) {
+
+	return (^T)(RawBufferGetMappedRange(buffer, offset, size_of(T)))
+}
+
+BufferGetMappedRangeSlice :: proc(buffer: Buffer, offset: uint, $T: typeid, length: uint) -> []T {
+	return ([^]T)(RawBufferGetMappedRange(buffer, offset, size_of(T)*length))[:length]
+}
+
+// Wrappers of ComputePassEncoder
+
+ComputePassEncoderSetBindGroup :: proc(computePassEncoder: ComputePassEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsets: []u32 = nil) {
+	RawComputePassEncoderSetBindGroup(computePassEncoder, groupIndex, group, len(dynamicOffsets), raw_data(dynamicOffsets))
+}
+
+// Wrappers of Device
+
+DeviceEnumerateFeatures :: proc(device: Device, allocator := context.allocator) -> []FeatureName {
+	count := RawDeviceEnumerateFeatures(device, nil)
+	features := make([]FeatureName, count, allocator)
+	RawDeviceEnumerateFeatures(device, raw_data(features))
+	return features
+}
+
+DeviceGetLimits :: proc(device: Device) -> (limits: SupportedLimits, ok: bool) {
+	ok = bool(RawDeviceGetLimits(device, &limits))
+	return
+}
+
+BufferWithDataDescriptor :: struct {
+	/* NULLABLE */ label: cstring,
+	usage: BufferUsageFlags,
+}
+
+DeviceCreateBufferWithDataSlice :: proc(device: Device, descriptor: /* const */ ^BufferWithDataDescriptor, data: []$T) -> (buf: Buffer) {
+	size := u64(size_of(T) * len(data))
+	buf = DeviceCreateBuffer(device, &{
+		label            = descriptor.label,
+		usage            = descriptor.usage,
+		size             = size,
+		mappedAtCreation = true,
+	})
+
+	mapping := BufferGetMappedRangeSlice(buf, 0, T, len(data))
+	copy(mapping, data)
+
+	BufferUnmap(buf)
+	return
+}
+
+DeviceCreateBufferWithDataTyped :: proc(device: Device, descriptor: /* const */ ^BufferWithDataDescriptor, data: $T) -> (buf: Buffer)
+	where !intrinsics.type_is_sliceable(T) {
+
+	buf = DeviceCreateBuffer(device, &{
+		label            = descriptor.label,
+		usage            = descriptor.usage,
+		size             = size_of(T),
+		mappedAtCreation = true,
+	})
+
+	mapping := BufferGetMappedRangeTyped(buf, 0, T)
+	mapping^ = data
+
+	BufferUnmap(buf)
+	return
+}
+
+DeviceCreateBufferWithData :: proc {
+	DeviceCreateBufferWithDataSlice,
+	DeviceCreateBufferWithDataTyped,
+}
+
+// Wrappers of Queue
+
+QueueSubmit :: proc(queue: Queue, commands: []CommandBuffer) {
+	RawQueueSubmit(queue, len(commands), raw_data(commands))
+}
+
+// Wrappers of RenderBundleEncoder
+
+RenderBundleEncoderSetBindGroup :: proc(renderBundleEncoder: RenderBundleEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsets: []u32 = nil) {
+	RawRenderBundleEncoderSetBindGroup(renderBundleEncoder, groupIndex, group, len(dynamicOffsets), raw_data(dynamicOffsets))
+}
+
+// Wrappers of RenderPassEncoder
+
+RenderPassEncoderExecuteBundles :: proc(renderPassEncoder: RenderPassEncoder, bundles: []RenderBundle) {
+	RawRenderPassEncoderExecuteBundles(renderPassEncoder, len(bundles), raw_data(bundles))
+}
+
+RenderPassEncoderSetBindGroup :: proc(renderPassEncoder: RenderPassEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsets: []u32 = nil) {
+	RawRenderPassEncoderSetBindGroup(renderPassEncoder, groupIndex, group, len(dynamicOffsets), raw_data(dynamicOffsets))
+}
+
+// Wrappers of Surface
+
+SurfaceGetCapabilities :: proc(surface: Surface, adapter: Adapter) -> (capabilities: SurfaceCapabilities) {
+	RawSurfaceGetCapabilities(surface, adapter, &capabilities)
+	return
+}
+
+SurfaceGetCurrentTexture :: proc(surface: Surface) -> (surface_texture: SurfaceTexture) {
+	RawSurfaceGetCurrentTexture(surface, &surface_texture)
+	return
+}

+ 26 - 0
vendor/wgpu/wgpu_js.odin

@@ -0,0 +1,26 @@
+package wgpu
+
+import "base:runtime"
+
+g_context: runtime.Context
+
+@(private="file", init)
+wgpu_init_allocator :: proc() {
+	if g_context.allocator.procedure == nil {
+		g_context = runtime.default_context()
+	}
+}
+
+@(private="file", export)
+wgpu_alloc :: proc "contextless" (size: i32) -> [^]byte {
+	context = g_context
+	bytes, err := runtime.mem_alloc(int(size), 16)
+	assert(err == nil, "wgpu_alloc failed")
+	return raw_data(bytes)
+}
+
+@(private="file", export)
+wgpu_free :: proc "contextless" (ptr: rawptr) {
+	context = g_context
+	assert(free(ptr) == nil, "wgpu_free failed")
+}

+ 75 - 0
vendor/wgpu/wgpu_native.odin

@@ -0,0 +1,75 @@
+//+build !js
+package wgpu
+
+BINDINGS_VERSION        :: [4]u8{0, 19, 4, 1}
+BINDINGS_VERSION_STRING :: "0.19.4.1"
+
+@(private="file", init)
+wgpu_native_version_check :: proc() {
+	v := (transmute([4]u8)GetVersion()).wzyx
+
+	if v != BINDINGS_VERSION {
+		buf: [1024]byte
+		n := copy(buf[:],  "wgpu-native version mismatch: ")
+		n += copy(buf[n:], "bindings are for version ")
+		n += copy(buf[n:], BINDINGS_VERSION_STRING)
+		n += copy(buf[n:], ", but a different version is linked")
+		panic(string(buf[:n]))
+	}
+}
+
+@(link_prefix="wgpu")
+foreign {
+	@(link_name="wgpuGenerateReport")
+	RawGenerateReport :: proc(instance: Instance, report: ^GlobalReport) ---
+	@(link_name="wgpuInstanceEnumerateAdapters")
+	RawInstanceEnumerateAdapters :: proc(instance: Instance, /* NULLABLE */ options: /* const */ ^InstanceEnumerateAdapterOptions, adapters: [^]Adapter) -> uint ---
+
+	@(link_name="wgpuQueueSubmitForIndex")
+	RawQueueSubmitForIndex :: proc(queue: Queue, commandCount: uint, commands: [^]CommandBuffer) -> SubmissionIndex ---
+
+	// Returns true if the queue is empty, or false if there are more queue submissions still in flight.
+	@(link_name="wgpuDevicePoll")
+	RawDevicePoll :: proc(device: Device, wait: b32, /* NULLABLE */ wrappedSubmissionIndex: /* const */ ^WrappedSubmissionIndex) -> b32 ---
+
+	SetLogCallback :: proc "odin" (callback: LogCallback) ---
+
+	SetLogLevel :: proc(level: LogLevel) ---
+
+	GetVersion :: proc() -> u32 ---
+
+	RenderPassEncoderSetPushConstants :: proc(encoder: RenderPassEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: cstring) ---
+
+	RenderPassEncoderMultiDrawIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) ---
+	RenderPassEncoderMultiDrawIndexedIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) ---
+
+	RenderPassEncoderMultiDrawIndirectCount :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count_buffer: Buffer, count_buffer_offset: u64, max_count: u32) ---
+	RenderPassEncoderMultiDrawIndexedIndirectCount :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count_buffer: Buffer, count_buffer_offset: u64, max_count: u32) ---
+
+	ComputePassEncoderBeginPipelineStatisticsQuery :: proc(computePassEncoder: ComputePassEncoder, querySet: QuerySet, queryIndex: u32) ---
+	ComputePassEncoderEndPipelineStatisticsQuery :: proc(computePassEncoder: ComputePassEncoder) ---
+	RenderPassEncoderBeginPipelineStatisticsQuery :: proc(renderPassEncoder: RenderPassEncoder, querySet: QuerySet, queryIndex: u32) ---
+	RenderPassEncoderEndPipelineStatisticsQuery :: proc(renderPassEncoder: RenderPassEncoder) ---
+}
+
+GenerateReport :: proc(instance: Instance) -> (report: GlobalReport) {
+	RawGenerateReport(instance, &report)
+	return
+}
+
+InstanceEnumerateAdapters :: proc(instance: Instance, options: ^InstanceEnumerateAdapterOptions = nil, allocator := context.allocator) -> (adapters: []Adapter) {
+	count := RawInstanceEnumerateAdapters(instance, options, nil)
+	adapters = make([]Adapter, count, allocator)
+	RawInstanceEnumerateAdapters(instance, options, raw_data(adapters))
+	return
+}
+
+QueueSubmitForIndex :: proc(queue: Queue, commands: []CommandBuffer) -> SubmissionIndex {
+	return RawQueueSubmitForIndex(queue, len(commands), raw_data(commands))
+}
+
+DevicePoll :: proc(device: Device, wait: b32) -> (wrappedSubmissionIndex: WrappedSubmissionIndex, ok: bool) {
+	ok = bool(RawDevicePoll(device, wait, &wrappedSubmissionIndex))
+	return
+}
+

+ 212 - 0
vendor/wgpu/wgpu_native_types.odin

@@ -0,0 +1,212 @@
+package wgpu
+
+import "base:runtime"
+
+LogLevel :: enum i32 {
+	Off,
+	Error,
+	Warn,
+	Info,
+	Debug,
+	Trace,
+}
+
+InstanceBackend :: enum i32 {
+	Vulkan,
+	GL,
+	Metal,
+	DX12,
+	DX11,
+	BrowserWebGPU,
+}
+InstanceBackendFlags :: bit_set[InstanceBackend; Flags]
+InstanceBackendFlags_All :: InstanceBackendFlags{}
+InstanceBackendFlags_Primary :: InstanceBackendFlags{ .Vulkan, .Metal, .DX12, .BrowserWebGPU }
+InstanceBackendFlags_Secondary :: InstanceBackendFlags{ .GL, .DX11 }
+
+InstanceFlag :: enum i32 {
+	Debug,
+	Validation,
+	DiscardHalLabels,
+}
+InstanceFlags :: bit_set[InstanceFlag; Flags]
+InstanceFlags_Default :: InstanceFlags{}
+
+Dx12Compiler :: enum i32 {
+	Undefined,
+	Fxc,
+	Dxc,
+}
+
+Gles3MinorVersion :: enum i32 {
+	Automatic,
+	Version0,
+	Version1,
+	Version2,
+}
+
+PipelineStatisticName :: enum i32 {
+	VertexShaderInvocations,
+	ClipperInvocations,
+	ClipperPrimitivesOut,
+	FragmentShaderInvocations,
+	ComputeShaderInvocations,
+}
+
+InstanceExtras :: struct {
+	using chain: ChainedStruct,
+	backends: InstanceBackendFlags,
+	flags: InstanceFlags,
+	dx12ShaderCompiler: Dx12Compiler,
+	gles3MinorVersion: Gles3MinorVersion,
+	dxilPath: cstring,
+	dxcPath: cstring,
+}
+
+DeviceExtras :: struct {
+	using chain: ChainedStruct,
+	tracePath: cstring,
+}
+
+NativeLimits :: struct {
+	maxPushConstantSize: u32,
+	maxNonSamplerBindings: u32,
+}
+
+RequiredLimitsExtras :: struct {
+	using chain: ChainedStruct,
+	limits: NativeLimits,
+}
+
+SupportedLimitsExtras :: struct {
+	using chain: ChainedStruct,
+	limits: NativeLimits,
+}
+
+PushConstantRange :: struct {
+	stages: ShaderStageFlags,
+	start: u32,
+	end: u32,
+}
+
+PipelineLayoutExtras :: struct {
+	using chain: ChainedStruct,
+	pushConstantRangeCount: uint,
+	pushConstantRanges: [^]PushConstantRange `fmt:"v,pushConstantRangeCount"`,
+}
+
+SubmissionIndex :: distinct u64
+
+WrappedSubmissionIndex :: struct {
+	queue: Queue,
+	submissionIndex: SubmissionIndex,
+}
+
+ShaderDefine :: struct {
+	name: cstring,
+	value: cstring,
+}
+
+ShaderModuleGLSLDescriptor :: struct {
+	using chain: ChainedStruct,
+	stage: ShaderStage,
+	code: cstring,
+	defineCount: uint,
+	defines: [^]ShaderDefine `fmt:"v,defineCount"`,
+}
+
+RegistryReport :: struct {
+	numAllocated: uint,
+	numKeptFromUser: uint,
+	numReleasedFromUser: uint,
+	numErrors: uint,
+	elementSize: uint,
+}
+
+HubReport :: struct {
+	adapters: RegistryReport,
+	devices: RegistryReport,
+	queues: RegistryReport,
+	pipelineLayouts: RegistryReport,
+	shaderModules: RegistryReport,
+	bindGroupLayouts: RegistryReport,
+	bindGroups: RegistryReport,
+	commandBuffers: RegistryReport,
+	renderBundles: RegistryReport,
+	renderPipelines: RegistryReport,
+	computePipelines: RegistryReport,
+	querySets: RegistryReport,
+	buffers: RegistryReport,
+	textures: RegistryReport,
+	textureViews: RegistryReport,
+	samplers: RegistryReport,
+}
+
+GlobalReport :: struct {
+	surfaces: RegistryReport,
+	backendType: BackendType,
+	vulkan: HubReport,
+	metal: HubReport,
+	dx12: HubReport,
+	gl: HubReport,
+}
+
+InstanceEnumerateAdapterOptions :: struct {
+	nextInChain: ^ChainedStruct,
+	backends: InstanceBackendFlags,
+}
+
+BindGroupEntryExtras :: struct {
+	using chain: ChainedStruct,
+	buffers: [^]Buffer `fmt:"v,bufferCount"`,
+	bufferCount: uint,
+	samplers: [^]Sampler `fmt:"v,samplerCount"`,
+	samplerCount: uint,
+	textureViews: [^]TextureView `fmt:"v,textureViewCount"`,
+	textureViewCount: uint,
+}
+
+BindGroupLayoutEntryExtras :: struct {
+	using chain: ChainedStruct,
+	count: u32,
+}
+
+QuerySetDescriptorExtras :: struct {
+	using chain: ChainedStruct,
+	pipelineStatistics: [^]PipelineStatisticName `fmt:"v,pipelineStatisticCount"`,
+	pipelineStatisticCount: uint,
+}
+
+SurfaceConfigurationExtras :: struct {
+	using chain: ChainedStruct,
+	desiredMaximumFrameLatency: i32,
+}
+
+LogCallback :: #type proc "odin" (level: LogLevel, message: cstring)
+
+// Wrappers
+
+ConvertOdinToWGPULogLevel :: proc(level: runtime.Logger_Level) -> LogLevel {
+	switch {
+	case level < .Debug:   return .Trace
+	case level < .Info:    return .Debug
+	case level < .Warning: return .Info
+	case level < .Error:   return .Warn
+	case:                  return .Error
+	}
+}
+
+ConvertWGPUToOdinLogLevel :: proc(level: LogLevel) -> runtime.Logger_Level {
+	switch level {
+	case .Off, .Trace, .Debug: return .Debug
+	case .Info:                return .Info
+	case .Warn:                return .Warning
+	case .Error:               return .Error
+	case:                      return .Error
+	}
+}
+
+ConvertLogLevel :: proc {
+	ConvertOdinToWGPULogLevel,
+	ConvertWGPUToOdinLogLevel,
+}

+ 3 - 0
vendor/x11/xlib/xlib_types.odin

@@ -24,6 +24,9 @@ Cursor   :: XID
 Colormap :: XID
 GContext :: XID
 
+RRCrtc   :: XID
+RROutput :: XID
+
 KeyCode  :: u8
 
 /* ----  X11/Xlib.h ---------------------------------------------------------*/