Browse Source

Merge pull request #3969 from elvodqa/master

Add `sdl2glue` to `vendor:wgpu` package
Laytan 1 year ago
parent
commit
772dce7e42

+ 8 - 1
vendor/wgpu/README.md

@@ -37,13 +37,20 @@ 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`.
+It exports one procedure `GetSurface(wgpu.Instance, glfw.WindowHandle) -> wgpu.Surface`.
 The procedure will call the needed target specific procedures and return a surface configured
 for the given window.
 
 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.
 
+## SDL2 Glue
+
+There is an inner package `sdl2glue` that can be used to glue together WGPU and SDL2.
+It exports one procedure `GetSurface(wgpu.Instance, ^sdl2.Window) -> wgpu.Surface`.
+The procedure will call the needed target specific procedures and return a surface configured
+for the given window.
+
 ### Wayland
 
 GLFW supports Wayland from version 3.4 onwards and only if it is compiled with `-DGLFW_EXPOSE_NATIVE_WAYLAND`.

+ 3 - 3
vendor/wgpu/example/Makefile → vendor/wgpu/examples/glfw/Makefile

@@ -8,10 +8,10 @@ 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
+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
+	cp ../../wgpu.js web/wgpu.js
+	cp ../../../wasm/js/runtime.js web/runtime.js

+ 2 - 2
vendor/wgpu/example/build.bat → vendor/wgpu/examples/glfw/build.bat

@@ -8,5 +8,5 @@ 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"
+copy "..\..\wgpu.js" "web\wgpu.js"
+copy "..\..\..\wasm\js\runtime.js" "web\runtime.js"

+ 0 - 0
vendor/wgpu/example/main.odin → vendor/wgpu/examples/glfw/main.odin


+ 0 - 0
vendor/wgpu/example/os_glfw.odin → vendor/wgpu/examples/glfw/os_glfw.odin


+ 0 - 0
vendor/wgpu/example/os_js.odin → vendor/wgpu/examples/glfw/os_js.odin


+ 0 - 0
vendor/wgpu/example/web/index.html → vendor/wgpu/examples/glfw/web/index.html


+ 17 - 0
vendor/wgpu/examples/sdl2/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/examples/sdl2/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/examples/sdl2/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)
+}

+ 60 - 0
vendor/wgpu/examples/sdl2/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()
+}

+ 85 - 0
vendor/wgpu/examples/sdl2/os_sdl2.odin

@@ -0,0 +1,85 @@
+//+build !js
+package vendor_wgpu_example_triangle
+
+import "core:c"
+import "core:fmt"
+
+import "vendor:sdl2"
+import "vendor:wgpu"
+import "vendor:wgpu/sdl2glue"
+
+OS :: struct {
+	window: ^sdl2.Window,
+}
+
+os_init :: proc(os: ^OS) {
+	sdl_flags := sdl2.InitFlags{.VIDEO, .JOYSTICK, .GAMECONTROLLER, .EVENTS}
+	if res := sdl2.Init(sdl_flags); res != 0 {
+		fmt.eprintfln("ERROR: Failed to initialize SDL: [%s]", sdl2.GetError())
+		return
+	}
+	
+	window_flags: sdl2.WindowFlags = {.SHOWN, .ALLOW_HIGHDPI, .RESIZABLE}
+	os.window = sdl2.CreateWindow(
+		"WGPU Native Triangle",
+		sdl2.WINDOWPOS_CENTERED,
+		sdl2.WINDOWPOS_CENTERED,
+		960,
+		540,
+		window_flags,
+	)
+	if os.window == nil {
+		fmt.eprintfln("ERROR: Failed to create the SDL Window: [%s]", sdl2.GetError())
+		return
+	}
+
+	sdl2.AddEventWatch(size_callback, nil)
+}
+
+os_run :: proc(os: ^OS) {
+	now := sdl2.GetPerformanceCounter()
+	last : u64
+	dt: f32
+	main_loop: for {
+		last = now
+		now = sdl2.GetPerformanceCounter()
+		dt = f32((now - last) * 1000) / f32(sdl2.GetPerformanceFrequency())
+
+		e: sdl2.Event
+
+		for sdl2.PollEvent(&e) {
+			#partial switch (e.type) {
+			case .QUIT:
+				break main_loop
+			}
+		}
+
+		frame(dt)
+	}
+
+	sdl2.DestroyWindow(os.window)
+	sdl2.Quit()
+
+	finish()
+}
+
+
+os_get_render_bounds :: proc(os: ^OS) -> (width, height: u32) {
+	iw, ih: c.int
+	sdl2.GetWindowSize(os.window, &iw, &ih)
+	return u32(iw), u32(ih)
+}
+
+os_get_surface :: proc(os: ^OS, instance: wgpu.Instance) -> wgpu.Surface {
+	return sdl2glue.GetSurface(instance, os.window)
+}
+
+@(private="file")
+size_callback :: proc "c" (userdata: rawptr, event: ^sdl2.Event) -> c.int {
+	if event.type == .WINDOWEVENT {
+		if event.window.event == .SIZE_CHANGED || event.window.event == .RESIZED {
+			resize()
+		}
+	}
+	return 0
+}

+ 23 - 0
vendor/wgpu/examples/sdl2/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/sdl2glue/glue.odin

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

+ 25 - 0
vendor/wgpu/sdl2glue/glue_darwin.odin

@@ -0,0 +1,25 @@
+package wgpu_sdl2_glue
+
+import    "vendor:sdl2"
+import    "vendor:wgpu"
+import CA "vendor:darwin/QuartzCore"
+import NS "core:sys/darwin/Foundation"
+
+GetSurface :: proc(instance: wgpu.Instance, window: ^sdl2.Window) -> wgpu.Surface {
+	window_info: sdl2.SysWMinfo 
+	sdl2.GetWindowWMInfo(window, &window_info)
+	ns_window := cast(^NS.Window)window_info.info.cocoa.window
+	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),
+			},
+		},
+	)
+}

+ 42 - 0
vendor/wgpu/sdl2glue/glue_linux.odin

@@ -0,0 +1,42 @@
+package wgpu_sdl2_glue
+
+import "vendor:sdl2"
+import "vendor:wgpu"
+
+GetSurface :: proc(instance: wgpu.Instance, window: ^sdl2.Window) -> wgpu.Surface {
+	window_info: sdl2.SysWMinfo 
+	sdl2.GetWindowWMInfo(window, &window_info)
+	if window_info.subsystem == .WAYLAND {
+		display := window_info.info.wl.display
+		surface := window_info.info.wl.surface
+		return wgpu.InstanceCreateSurface(
+			instance,
+			&wgpu.SurfaceDescriptor{
+				nextInChain = &wgpu.SurfaceDescriptorFromWaylandSurface{
+					chain = {
+						sType = .SurfaceDescriptorFromWaylandSurface,
+					},
+					display = display,
+					surface = surface,
+				},
+			},
+		)
+	} else if window_info.subsystem == .X11 {
+		display := window_info.info.x11.display
+		window  := window_info.info.x11.window
+		return wgpu.InstanceCreateSurface(
+			instance,
+			&wgpu.SurfaceDescriptor{
+				nextInChain = &wgpu.SurfaceDescriptorFromXlibWindow{
+					chain = {
+						sType = .SurfaceDescriptorFromXlibWindow,
+					},
+					display = display,
+					window  = u64(window),
+				},
+			},
+		)
+	} else {
+		panic("wgpu sdl2 glue: unsupported platform, expected Wayland or X11")
+	}
+}

+ 25 - 0
vendor/wgpu/sdl2glue/glue_windows.odin

@@ -0,0 +1,25 @@
+package wgpu_sdl2_glue
+
+import win "core:sys/windows"
+
+import  "vendor:sdl2"
+import  "vendor:wgpu"
+
+GetSurface :: proc(instance: wgpu.Instance, window: ^sdl2.Window) -> wgpu.Surface {
+	window_info: sdl2.SysWMinfo 
+	sdl2.GetWindowWMInfo(window, &window_info)
+	hwnd := window_info.info.win.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),
+			},
+		},
+	)
+}