瀏覽代碼

Merge branch 'master' of https://github.com/avanspector/Odin

avanspector 8 月之前
父節點
當前提交
9868c8292b

+ 41 - 9
core/mem/tracking_allocator.odin

@@ -34,12 +34,18 @@ Tracking_Allocator_Bad_Free_Entry :: struct {
 	location: runtime.Source_Code_Location,
 }
 
+/*
+Callback type for when tracking allocator runs into a bad free.
+*/
+Tracking_Allocator_Bad_Free_Callback :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location)
+
 /*
 Tracking allocator data.
 */
 Tracking_Allocator :: struct {
 	backing: Allocator,
 	allocation_map: map[rawptr]Tracking_Allocator_Entry,
+	bad_free_callback: Tracking_Allocator_Bad_Free_Callback,
 	bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
 	mutex: sync.Mutex,
 	clear_on_free_all: bool,
@@ -61,6 +67,7 @@ allocate the tracked data.
 tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
 	t.backing = backing_allocator
 	t.allocation_map.allocator = internals_allocator
+	t.bad_free_callback = tracking_allocator_bad_free_callback_panic
 	t.bad_free_array.allocator = internals_allocator
 	if .Free_All in query_features(t.backing) {
 		t.clear_on_free_all = true
@@ -109,6 +116,33 @@ tracking_allocator_reset :: proc(t: ^Tracking_Allocator) {
 	sync.mutex_unlock(&t.mutex)
 }
 
+/*
+Default behavior for a bad free: Crash with error message that says where the
+bad free happened.
+
+Override Tracking_Allocator.bad_free_callback to have something else happen. For
+example, you can use tracking_allocator_bad_free_callback_add_to_array to return
+the tracking allocator to the old behavior, where the bad_free_array was used.
+*/
+tracking_allocator_bad_free_callback_panic :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) {
+	runtime.print_caller_location(location)
+	runtime.print_string(" Tracking allocator error: Bad free of pointer ")
+	runtime.print_uintptr(uintptr(memory))
+	runtime.print_string("\n")
+	runtime.trap()
+}
+
+/*
+Alternative behavior for a bad free: Store in `bad_free_array`. If you use this,
+then you must make sure to check Tracking_Allocator.bad_free_array at some point.
+*/
+tracking_allocator_bad_free_callback_add_to_array :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) {
+	append(&t.bad_free_array, Tracking_Allocator_Bad_Free_Entry {
+		memory = memory,
+		location = location,
+	})
+}
+
 /*
 Tracking allocator.
 
@@ -116,8 +150,10 @@ The tracking allocator is an allocator wrapper that tracks memory allocations.
 This allocator stores all the allocations in a map. Whenever a pointer that's
 not inside of the map is freed, the `bad_free_array` entry is added.
 
-An example of how to use the `Tracking_Allocator` to track subsequent allocations
-in your program and report leaks and bad frees:
+Here follows an example of how to use the `Tracking_Allocator` to track
+subsequent allocations in your program and report leaks. By default, the
+tracking allocator will crash on bad frees. You can override that behavior by
+overriding `track.bad_free_callback`.
 
 Example:
 
@@ -137,9 +173,6 @@ Example:
 		for _, leak in track.allocation_map {
 			fmt.printf("%v leaked %m\n", leak.location, leak.size)
 		}
-		for bad_free in track.bad_free_array {
-			fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
-		}
 	}
 */
 @(require_results)
@@ -191,10 +224,9 @@ tracking_allocator_proc :: proc(
 	}
 
 	if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
-		append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
-			memory = old_memory,
-			location = loc,
-		})
+		if data.bad_free_callback != nil {
+			data.bad_free_callback(data, old_memory, loc)
+		}
 	} else {
 		result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
 	}

+ 11 - 5
core/strings/intern.odin

@@ -89,6 +89,7 @@ intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runt
 	entry := _intern_get_entry(m, text) or_return
 	return cstring(&entry.str[0]), nil
 }
+
 /*
 Internal function to lookup whether the text string exists in the map, returns the entry
 Sets and allocates the entry if it wasn't set yet
@@ -104,13 +105,15 @@ Returns:
 - err: An allocator error if one occured, `nil` otherwise
 */
 _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
-	if prev, ok := m.entries[text]; ok {
-		return prev, nil
-	}
 	if m.allocator.procedure == nil {
 		m.allocator = context.allocator
 	}
 
+	key_ptr, val_ptr, inserted := map_entry(&m.entries, text) or_return
+	if !inserted {
+		return val_ptr^, nil
+	}
+
 	entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1
 	bytes := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) or_return
 	new_entry = (^Intern_Entry)(raw_data(bytes))
@@ -120,6 +123,9 @@ _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry
 	new_entry.str[new_entry.len] = 0
 
 	key := string(new_entry.str[:new_entry.len])
-	m.entries[key] = new_entry
-	return new_entry, nil
+
+	key_ptr^ = key
+	val_ptr^ = new_entry
+
+	return
 }

+ 4 - 1
core/sys/wasm/js/odin.js

@@ -110,7 +110,10 @@ class WasmMemoryInterface {
 	}
 
 	loadCstring(ptr) {
-		const start = this.loadPtr(ptr);
+		return this.loadCstringDirect(this.loadPtr(ptr));
+	}
+
+	loadCstringDirect(start) {
 		if (start == 0) {
 			return null;
 		}

+ 1 - 0
core/sys/windows/comctl32.odin

@@ -6,4 +6,5 @@ foreign import "system:Comctl32.lib"
 @(default_calling_convention="system")
 foreign Comctl32 {
 	LoadIconWithScaleDown :: proc(hinst: HINSTANCE, pszName: PCWSTR, cx: c_int, cy: c_int, phico: ^HICON) -> HRESULT ---
+	SetWindowSubclass :: proc(hwnd: HWND, pfnSubclass: SUBCLASSPROC, uIdSubclass: UINT_PTR, dwRefData: DWORD_PTR) ---
 }

+ 1 - 1
core/sys/windows/dbghelp.odin

@@ -15,7 +15,7 @@ MINIDUMP_DIRECTORY :: struct {
 	Location:   MINIDUMP_LOCATION_DESCRIPTOR,
 }
 
-MINIDUMP_EXCEPTION_INFORMATION :: struct {
+MINIDUMP_EXCEPTION_INFORMATION :: struct #max_field_align(4) {
 	ThreadId:          DWORD,
 	ExceptionPointers: ^EXCEPTION_POINTERS,
 	ClientPointers:    BOOL,

+ 2 - 0
core/sys/windows/types.odin

@@ -796,6 +796,8 @@ TIMERPROC :: #type proc "system" (HWND, UINT, UINT_PTR, DWORD)
 
 WNDPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
 
+SUBCLASSPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM, UINT_PTR, DWORD_PTR) -> LRESULT
+
 HOOKPROC :: #type proc "system" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT
 
 WINEVENTPROC :: #type proc "system" (

+ 4 - 1
core/sys/windows/user32.odin

@@ -2,6 +2,7 @@
 package sys_windows
 
 import "base:intrinsics"
+import "core:c"
 foreign import user32 "system:User32.lib"
 
 @(default_calling_convention="system")
@@ -32,6 +33,8 @@ foreign user32 {
 	RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
 	UnregisterClassW :: proc(lpClassName: LPCWSTR, hInstance: HINSTANCE) -> BOOL ---
 
+	RegisterHotKey :: proc(hnwd: HWND, id: c.int, fsModifiers: UINT, vk: UINT) -> BOOL ---
+
 	CreateWindowExW :: proc(
 		dwExStyle: DWORD,
 		lpClassName: LPCWSTR,
@@ -553,7 +556,7 @@ MOUSE_ATTRIBUTES_CHANGED :: 0x04
 MOUSE_MOVE_NOCOALESCE :: 0x08
 
 RI_MOUSE_BUTTON_1_DOWN :: 0x0001
-RI_MOUSE_LEFT_BUTTON_DOWNS :: RI_MOUSE_BUTTON_1_DOWN
+RI_MOUSE_LEFT_BUTTON_DOWN :: RI_MOUSE_BUTTON_1_DOWN
 RI_MOUSE_BUTTON_1_UP :: 0x0002
 RI_MOUSE_LEFT_BUTTON_UP :: RI_MOUSE_BUTTON_1_UP
 RI_MOUSE_BUTTON_2_DOWN :: 0x0004

+ 210 - 0
core/sys/windows/xinput.odin

@@ -0,0 +1,210 @@
+#+build windows
+package sys_windows
+
+foreign import "system:xinput.lib"
+
+// Device types available in XINPUT_CAPABILITIES
+// Correspond to XINPUT_DEVTYPE_...
+XINPUT_DEVTYPE :: enum BYTE {
+	GAMEPAD = 0x01,
+}
+
+// Device subtypes available in XINPUT_CAPABILITIES
+// Correspond to XINPUT_DEVSUBTYPE_...
+XINPUT_DEVSUBTYPE :: enum BYTE {
+	UNKNOWN          = 0x00,
+	GAMEPAD          = 0x01,
+	WHEEL            = 0x02,
+	ARCADE_STICK     = 0x03,
+	FLIGHT_STICK     = 0x04,
+	DANCE_PAD        = 0x05,
+	GUITAR           = 0x06,
+	GUITAR_ALTERNATE = 0x07,
+	DRUM_KIT         = 0x08,
+	GUITAR_BASS      = 0x0B,
+	ARCADE_PAD       = 0x13,
+}
+
+// Flags for XINPUT_CAPABILITIES
+// Correspond to log2(XINPUT_CAPS_...)
+XINPUT_CAP :: enum WORD {
+	FFB_SUPPORTED   = 0,
+	WIRELESS        = 1,
+	VOICE_SUPPORTED = 2,
+	PMD_SUPPORTED   = 3,
+	NO_NAVIGATION   = 4,
+}
+XINPUT_CAPS :: distinct bit_set[XINPUT_CAP;WORD]
+
+// Constants for gamepad buttons
+// Correspond to log2(XINPUT_GAMEPAD_...)
+XINPUT_GAMEPAD_BUTTON_BIT :: enum WORD {
+	DPAD_UP        = 0,
+	DPAD_DOWN      = 1,
+	DPAD_LEFT      = 2,
+	DPAD_RIGHT     = 3,
+	START          = 4,
+	BACK           = 5,
+	LEFT_THUMB     = 6,
+	RIGHT_THUMB    = 7,
+	LEFT_SHOULDER  = 8,
+	RIGHT_SHOULDER = 9,
+	A              = 12,
+	B              = 13,
+	X              = 14,
+	Y              = 15,
+}
+XINPUT_GAMEPAD_BUTTON :: distinct bit_set[XINPUT_GAMEPAD_BUTTON_BIT;WORD]
+
+// Gamepad thresholds
+XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE: SHORT : 7849
+XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE: SHORT : 8689
+XINPUT_GAMEPAD_TRIGGER_THRESHOLD: SHORT : 30
+
+// Flags to pass to XInputGetCapabilities
+// Corresponds to log2(XINPUT_FLAG_...)
+XINPUT_FLAG_BIT :: enum WORD {
+	GAMEPAD = 0,
+}
+XINPUT_FLAG :: distinct bit_set[XINPUT_FLAG_BIT;DWORD]
+
+// Devices that support batteries
+// Corresponds to BATTERY_DEVTYPE_...
+BATTERY_DEVTYPE :: enum BYTE {
+	GAMEPAD = 0x00,
+	HEADSET = 0x01,
+}
+
+// Flags for battery status level
+// Correspond to BATTERY_TYPE_...
+BATTERY_TYPE :: enum BYTE {
+	DISCONNECTED = 0x00, // This device is not connected
+	WIRED        = 0x01, // Wired device, no battery
+	ALKALINE     = 0x02, // Alkaline battery source
+	NIMH         = 0x03, // Nickel Metal Hydride battery source
+	UNKNOWN      = 0xFF, // Cannot determine the battery type
+}
+
+// These are only valid for wireless, connected devices, with known battery types
+// The amount of use time remaining depends on the type of device.
+// Correspond to BATTERY_LEVEL_...
+BATTERY_LEVEL :: enum BYTE {
+	EMPTY  = 0x00,
+	LOW    = 0x01,
+	MEDIUM = 0x02,
+	FULL   = 0x03,
+}
+
+// User index definitions
+
+// Index of the gamer associated with the device
+XUSER :: enum DWORD {
+	One   = 0,
+	Two   = 1,
+	Three = 2,
+	Four  = 3,
+	Any   = 0x000000FF, // Can be only used with XInputGetKeystroke
+}
+
+XUSER_MAX_COUNT :: 4
+
+// Codes returned for the gamepad keystroke
+// Corresponds to VK_PAD_...
+VK_PAD :: enum WORD {
+	A                = 0x5800,
+	B                = 0x5801,
+	X                = 0x5802,
+	Y                = 0x5803,
+	RSHOULDER        = 0x5804,
+	LSHOULDER        = 0x5805,
+	LTRIGGER         = 0x5806,
+	RTRIGGER         = 0x5807,
+	DPAD_UP          = 0x5810,
+	DPAD_DOWN        = 0x5811,
+	DPAD_LEFT        = 0x5812,
+	DPAD_RIGHT       = 0x5813,
+	START            = 0x5814,
+	BACK             = 0x5815,
+	LTHUMB_PRESS     = 0x5816,
+	RTHUMB_PRESS     = 0x5817,
+	LTHUMB_UP        = 0x5820,
+	LTHUMB_DOWN      = 0x5821,
+	LTHUMB_RIGHT     = 0x5822,
+	LTHUMB_LEFT      = 0x5823,
+	LTHUMB_UPLEFT    = 0x5824,
+	LTHUMB_UPRIGHT   = 0x5825,
+	LTHUMB_DOWNRIGHT = 0x5826,
+	LTHUMB_DOWNLEFT  = 0x5827,
+	RTHUMB_UP        = 0x5830,
+	RTHUMB_DOWN      = 0x5831,
+	RTHUMB_RIGHT     = 0x5832,
+	RTHUMB_LEFT      = 0x5833,
+	RTHUMB_UPLEFT    = 0x5834,
+	RTHUMB_UPRIGHT   = 0x5835,
+	RTHUMB_DOWNRIGHT = 0x5836,
+	RTHUMB_DOWNLEFT  = 0x5837,
+}
+
+// Flags used in XINPUT_KEYSTROKE
+// Correspond to log2(XINPUT_KEYSTROKE_...)
+XINPUT_KEYSTROKE_BIT :: enum WORD {
+	KEYDOWN = 0,
+	KEYUP   = 1,
+	REPEAT  = 2,
+}
+XINPUT_KEYSTROKES :: distinct bit_set[XINPUT_KEYSTROKE_BIT;WORD]
+
+// Structures used by XInput APIs
+XINPUT_GAMEPAD :: struct {
+	wButtons:      XINPUT_GAMEPAD_BUTTON,
+	bLeftTrigger:  BYTE,
+	bRightTrigger: BYTE,
+	sThumbLX:      SHORT,
+	sThumbLY:      SHORT,
+	sThumbRX:      SHORT,
+	sThumbRY:      SHORT,
+}
+
+XINPUT_STATE :: struct {
+	dwPacketNumber: DWORD,
+	Gamepad:        XINPUT_GAMEPAD,
+}
+
+XINPUT_VIBRATION :: struct {
+	wLeftMotorSpeed:  WORD,
+	wRightMotorSpeed: WORD,
+}
+
+XINPUT_CAPABILITIES :: struct {
+	Type:      XINPUT_DEVTYPE,
+	SubType:   XINPUT_DEVSUBTYPE,
+	Flags:     XINPUT_CAPS,
+	Gamepad:   XINPUT_GAMEPAD,
+	Vibration: XINPUT_VIBRATION,
+}
+
+XINPUT_BATTERY_INFORMATION :: struct {
+	BatteryType:  BATTERY_TYPE,
+	BatteryLevel: BATTERY_LEVEL,
+}
+
+XINPUT_KEYSTROKE :: struct {
+	VirtualKey: VK_PAD,
+	Unicode:    WCHAR,
+	Flags:      XINPUT_KEYSTROKES,
+	UserIndex:  XUSER,
+	HidCode:    BYTE,
+}
+
+// XInput APIs
+@(default_calling_convention = "system")
+foreign xinput {
+	XInputGetState :: proc(user: XUSER, pState: ^XINPUT_STATE) -> System_Error ---
+	XInputSetState :: proc(user: XUSER, pVibration: ^XINPUT_VIBRATION) -> System_Error ---
+	XInputGetCapabilities :: proc(user: XUSER, dwFlags: XINPUT_FLAG, pCapabilities: ^XINPUT_CAPABILITIES) -> System_Error ---
+	XInputEnable :: proc(enable: BOOL) ---
+	XInputGetAudioDeviceIds :: proc(user: XUSER, pRenderDeviceId: LPWSTR, pRenderCount: ^UINT, pCaptureDeviceId: LPWSTR, pCaptureCount: ^UINT) -> System_Error ---
+	XInputGetBatteryInformation :: proc(user: XUSER, devType: BATTERY_DEVTYPE, pBatteryInformation: ^XINPUT_BATTERY_INFORMATION) -> System_Error ---
+	XInputGetKeystroke :: proc(user: XUSER, dwReserved: DWORD, pKeystroke: ^XINPUT_KEYSTROKE) -> System_Error ---
+	XInputGetDSoundAudioDeviceGuids :: proc(user: XUSER, pDSoundRenderGuid: ^GUID, pDSoundCaptureGuid: ^GUID) -> System_Error ---
+}

+ 1 - 0
core/testing/runner.odin

@@ -391,6 +391,7 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
 		fmt.assertf(alloc_error == nil, "Error allocating memory for task allocator #%i: %v", i, alloc_error)
 		when TRACKING_MEMORY {
 			mem.tracking_allocator_init(&task_memory_trackers[i], mem.rollback_stack_allocator(&task_allocators[i]))
+			task_memory_trackers[i].bad_free_callback = mem.tracking_allocator_bad_free_callback_add_to_array
 		}
 	}
 

+ 8 - 5
src/llvm_backend_debug.cpp

@@ -408,13 +408,18 @@ gb_internal LLVMMetadataRef lb_debug_union(lbModule *m, Type *type, String name,
 	lb_set_llvm_metadata(m, type, temp_forward_decl);
 
 	isize index_offset = 1;
+	isize variant_offset = 1;
 	if (is_type_union_maybe_pointer(bt)) {
 		index_offset = 0;
+		variant_offset = 0;
+	} else if (bt->Union.kind == UnionType_no_nil) {
+		variant_offset = 0;
 	}
 
 	LLVMMetadataRef member_scope = lb_get_llvm_metadata(m, bt->Union.scope);
 	unsigned element_count = cast(unsigned)bt->Union.variants.count;
 	if (index_offset > 0) {
+		GB_ASSERT(index_offset == 1);
 		element_count += 1;
 	}
 
@@ -437,13 +442,11 @@ gb_internal LLVMMetadataRef lb_debug_union(lbModule *m, Type *type, String name,
 	for_array(j, bt->Union.variants) {
 		Type *variant = bt->Union.variants[j];
 
-		unsigned field_index = cast(unsigned)(index_offset+j);
-
-		char name[16] = {};
-		gb_snprintf(name, gb_size_of(name), "v%u", field_index);
+		char name[32] = {};
+		gb_snprintf(name, gb_size_of(name), "v%td", variant_offset+j);
 		isize name_len = gb_strlen(name);
 
-		elements[field_index] = LLVMDIBuilderCreateMemberType(
+		elements[index_offset+j] = LLVMDIBuilderCreateMemberType(
 			m->debug_builder, member_scope,
 			name, name_len,
 			file, line,

+ 1 - 0
tests/core/container/test_core_rbtree.odin

@@ -11,6 +11,7 @@ import "core:log"
 test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
 	track: mem.Tracking_Allocator
 	mem.tracking_allocator_init(&track, context.allocator)
+	track.bad_free_callback = mem.tracking_allocator_bad_free_callback_add_to_array
 	defer mem.tracking_allocator_destroy(&track)
 	context.allocator = mem.tracking_allocator(&track)
 

+ 12 - 12
vendor/wgpu/wgpu.js

@@ -1181,7 +1181,7 @@ class WebGPUInterface {
 			 */
 			wgpuBufferSetLabel: (bufferIdx, labelPtr) => {
 				const buffer = this.buffers.get(bufferIdx);
-				buffer.buffer.label = this.mem.loadCstring(labelPtr);
+				buffer.buffer.label = this.mem.loadCstringDirect(labelPtr);
 			},
 
 			/**
@@ -1370,7 +1370,7 @@ class WebGPUInterface {
 			 */
 			wgpuCommandEncoderInsertDebugMarker: (commandEncoderIdx, markerLabelPtr) => {
 				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
-				commandEncoder.insertDebugMarker(this.mem.loadCstring(markerLabelPtr));
+				commandEncoder.insertDebugMarker(this.mem.loadCstringDirect(markerLabelPtr));
 			},
 
 			/**
@@ -1387,7 +1387,7 @@ class WebGPUInterface {
 			 */
 			wgpuCommandEncoderPushDebugGroup: (commandEncoderIdx, groupLabelPtr) => {
 				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
-				commandEncoder.pushDebugGroup(this.mem.loadCstring(groupLabelPtr));
+				commandEncoder.pushDebugGroup(this.mem.loadCstringDirect(groupLabelPtr));
 			},
 
 			/**
@@ -1459,7 +1459,7 @@ class WebGPUInterface {
 			 */
 			wgpuComputePassEncoderInsertDebugMarker: (computePassEncoderIdx, markerLabelPtr) => {
 				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
-				computePassEncoder.insertDebugMarker(this.mem.loadCstring(markerLabelPtr));
+				computePassEncoder.insertDebugMarker(this.mem.loadCstringDirect(markerLabelPtr));
 			},
 
 			/**
@@ -1476,7 +1476,7 @@ class WebGPUInterface {
 			 */
 			wgpuComputePassEncoderPushDebugGroup: (computePassEncoderIdx, groupLabelPtr) => {
 				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
-				computePassEncoder.pushDebugGroup(this.mem.loadCstring(groupLabelPtr));
+				computePassEncoder.pushDebugGroup(this.mem.loadCstringDirect(groupLabelPtr));
 			},
 
 			/**
@@ -2216,7 +2216,7 @@ class WebGPUInterface {
 			wgpuRenderBundleEncoderInsertDebugMarker: (renderBundleEncoderIdx, markerLabelPtr) => {
 				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
 				this.assert(markerLabelPtr != 0);
-				const markerLabel = this.mem.loadCstring(markerLabelPtr);
+				const markerLabel = this.mem.loadCstringDirect(markerLabelPtr);
 				renderBundleEncoder.insertDebugMarker(markerLabel);
 			},
 
@@ -2235,7 +2235,7 @@ class WebGPUInterface {
 			wgpuRenderBundleEncoderPushDebugGroup: (renderBundleEncoderIdx, groupLabelPtr) => {
 				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
 				this.assert(groupLabelPtr!= 0);
-				const groupLabel = this.mem.loadCstring(groupLabelPtr);
+				const groupLabel = this.mem.loadCstringDirect(groupLabelPtr);
 				renderBundleEncoder.pushDebugGroup(groupLabel);
 			},
 
@@ -2407,7 +2407,7 @@ class WebGPUInterface {
 			 */
 			wgpuRenderPassEncoderInsertDebugMarker: (renderPassEncoderIdx, markerLabelPtr) => {
 				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
-				const markerLabel = this.mem.loadCstring(markerLabelPtr);
+				const markerLabel = this.mem.loadCstringDirect(markerLabelPtr);
 				renderPassEncoder.insertDebugMarker(markerLabel);
 			},
 
@@ -2425,7 +2425,7 @@ class WebGPUInterface {
 			 */
 			wgpuRenderPassEncoderPushDebugGroup: (renderPassEncoderIdx, groupLabelPtr) => {
 				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
-				const groupLabel = this.mem.loadCstring(groupLabelPtr);
+				const groupLabel = this.mem.loadCstringDirect(groupLabelPtr);
 				renderPassEncoder.pushDebugGroup(groupLabel);
 			},
 
@@ -2881,11 +2881,11 @@ class WebGPUObjectManager {
 	}
 
 	/**
-	 * @param {number} idx
+	 * @param {?number} idx
 	 * @returns {T}
 	 */
 	get(idx) {
-		return this.objects[idx-1].object;
+		return this.objects[idx-1]?.object;
 	}
 
 	/** @param {number} idx */
@@ -2908,7 +2908,7 @@ class WebGPUObjectManager {
 		if (withLabelSetter) {
 			inter[`wgpu${this.name}SetLabel`] = (idx, labelPtr) => {
 				const obj = this.get(idx);
-				obj.label = this.mem.loadCstring(labelPtr);
+				obj.label = this.mem.loadCstringDirect(labelPtr);
 			};
 		}
 		return inter;