Skip to content

Commit 9faea5a

Browse files
committed
move await and bytestojs to separate jsx package
1 parent bbbce18 commit 9faea5a

File tree

7 files changed

+72
-55
lines changed

7 files changed

+72
-55
lines changed

jsx/jsx.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//go:build js
2+
3+
// Package jsx provides essential JavaScript functions that are used
4+
// widely in wgpu and are very useful for an wasm / js application.
5+
package jsx
6+
7+
import (
8+
"log/slog"
9+
"syscall/js"
10+
"unsafe"
11+
)
12+
13+
// BytesToJS converts the given bytes to a js Uint8ClampedArray
14+
// by using the global wasm memory bytes. This avoids the
15+
// copying present in [js.CopyBytesToJS].
16+
func BytesToJS(b []byte) js.Value {
17+
ptr := uintptr(unsafe.Pointer(&b[0]))
18+
// We directly pass the offset and length to the constructor to avoid calling subarray or slice,
19+
// thereby improving performance and safety (this fixes a detached array buffer crash).
20+
return js.Global().Get("Uint8ClampedArray").New(js.Global().Get("wasm").Get("instance").Get("exports").Get("mem").Get("buffer"), ptr, len(b))
21+
}
22+
23+
// Await is a helper function equivalent to await in JS.
24+
// It is copied from https://go-review.googlesource.com/c/go/+/150917/
25+
func Await(promise js.Value) (result js.Value, ok bool) {
26+
if promise.Type() != js.TypeObject || promise.Get("then").Type() != js.TypeFunction {
27+
return promise, true
28+
}
29+
30+
done := make(chan struct{})
31+
32+
onResolve := js.FuncOf(func(this js.Value, args []js.Value) any {
33+
result = args[0]
34+
ok = true
35+
close(done)
36+
return nil
37+
})
38+
defer onResolve.Release()
39+
40+
onReject := js.FuncOf(func(this js.Value, args []js.Value) any {
41+
result = args[0]
42+
ok = false
43+
slog.Error("wgpu.AwaitJS: promise rejected", "reason", result)
44+
close(done)
45+
return nil
46+
})
47+
defer onReject.Release()
48+
49+
promise.Call("then", onResolve, onReject)
50+
<-done
51+
return
52+
}

wgpu/adapter_js.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ package wgpu
55
import (
66
"fmt"
77
"syscall/js"
8+
9+
"github.com/cogentcore/webgpu/jsx"
810
)
911

1012
// Adapter as described:
@@ -14,7 +16,7 @@ type Adapter struct {
1416
}
1517

1618
func (g Adapter) RequestDevice(descriptor *DeviceDescriptor) (*Device, error) {
17-
device, ok := AwaitJS(g.jsValue.Call("requestDevice", pointerToJS(descriptor)))
19+
device, ok := jsx.Await(g.jsValue.Call("requestDevice", pointerToJS(descriptor)))
1820
if !ok || !device.Truthy() {
1921
return nil, fmt.Errorf("no WebGPU device avaliable")
2022
}

wgpu/buffer_js.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
package wgpu
44

5-
import "syscall/js"
5+
import (
6+
"syscall/js"
7+
8+
"github.com/cogentcore/webgpu/jsx"
9+
)
610

711
// Buffer as described:
812
// https://gpuweb.github.io/gpuweb/#gpubuffer
@@ -32,7 +36,7 @@ func (g Buffer) GetMappedRange(offset, size uint) []byte {
3236
}
3337

3438
func (g Buffer) MapAsync(mode MapMode, offset uint64, size uint64, callback BufferMapCallback) (err error) {
35-
AwaitJS(g.jsValue.Call("mapAsync", uint32(mode), offset, size))
39+
jsx.Await(g.jsValue.Call("mapAsync", uint32(mode), offset, size))
3640
callback(BufferMapAsyncStatusSuccess) // TODO(kai): is this the right thing to do?
3741
return
3842
}

wgpu/device_ext_js.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
package wgpu
44

5-
import "syscall/js"
5+
import (
6+
"syscall/js"
7+
)
68

79
// TODO(kai): this only needs to be separate for js because
810
// [Buffer.GetMappedRange] does not work correctly without GopherJS.

wgpu/instance_js.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"fmt"
77
"log"
88
"syscall/js"
9+
10+
"github.com/cogentcore/webgpu/jsx"
911
)
1012

1113
// Instance as described:
@@ -25,7 +27,7 @@ func CreateInstance(descriptor *InstanceDescriptor) *Instance {
2527
}
2628

2729
func (g Instance) RequestAdapter(options *RequestAdapterOptions) (*Adapter, error) {
28-
adapter, ok := AwaitJS(g.jsValue.Call("requestAdapter", pointerToJS(options)))
30+
adapter, ok := jsx.Await(g.jsValue.Call("requestAdapter", pointerToJS(options)))
2931
if !ok || !adapter.Truthy() {
3032
return nil, fmt.Errorf("no WebGPU adapter avaliable")
3133
}

wgpu/queue_js.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ package wgpu
44

55
import (
66
"syscall/js"
7+
8+
"github.com/cogentcore/webgpu/jsx"
79
)
810

911
// Queue as described:
@@ -28,21 +30,21 @@ func (g Queue) Submit(commandBuffers ...*CommandBuffer) {
2830
// WriteBuffer as described:
2931
// https://gpuweb.github.io/gpuweb/#dom-gpuqueue-writebuffer
3032
func (g Queue) WriteBuffer(buffer *Buffer, offset uint64, data []byte) (err error) {
31-
g.jsValue.Call("writeBuffer", pointerToJS(buffer), offset, BytesToJS(data), uint64(0), len(data))
33+
g.jsValue.Call("writeBuffer", pointerToJS(buffer), offset, jsx.BytesToJS(data), uint64(0), len(data))
3234
return
3335
}
3436

3537
// WriteTexture as described:
3638
// https://gpuweb.github.io/gpuweb/#dom-gpuqueue-writetexture
3739
func (g Queue) WriteTexture(destination *ImageCopyTexture, data []byte, dataLayout *TextureDataLayout, writeSize *Extent3D) (err error) {
38-
g.jsValue.Call("writeTexture", pointerToJS(destination), BytesToJS(data), pointerToJS(dataLayout), pointerToJS(writeSize))
40+
g.jsValue.Call("writeTexture", pointerToJS(destination), jsx.BytesToJS(data), pointerToJS(dataLayout), pointerToJS(writeSize))
3941
return
4042
}
4143

4244
// OnSubmittedWorkDone as described:
4345
// https://gpuweb.github.io/gpuweb/#dom-gpuqueue-onsubmittedworkdone
4446
func (g Queue) OnSubmittedWorkDone(callback QueueWorkDoneCallback) {
45-
AwaitJS(g.jsValue.Call("onSubmittedWorkDone")) // TODO(kai): is this correct?
47+
jsx.Await(g.jsValue.Call("onSubmittedWorkDone")) // TODO(kai): is this correct?
4648
callback(QueueWorkDoneStatusSuccess)
4749
}
4850

wgpu/util_js.go

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,6 @@
22

33
package wgpu
44

5-
import (
6-
"log/slog"
7-
"syscall/js"
8-
"unsafe"
9-
)
10-
11-
// BytesToJS converts the given bytes to a js Uint8ClampedArray
12-
// by using the global wasm memory bytes. This avoids the
13-
// copying present in [js.CopyBytesToJS].
14-
func BytesToJS(b []byte) js.Value {
15-
ptr := uintptr(unsafe.Pointer(&b[0]))
16-
// We directly pass the offset and length to the constructor to avoid calling subarray or slice,
17-
// thereby improving performance and safety (this fixes a detached array buffer crash).
18-
return js.Global().Get("Uint8ClampedArray").New(js.Global().Get("wasm").Get("instance").Get("exports").Get("mem").Get("buffer"), ptr, len(b))
19-
}
20-
215
// mapSlice can be used to transform one slice into another by providing a
226
// function to do the mapping.
237
func mapSlice[S, T any](slice []S, fn func(S) T) []T {
@@ -31,37 +15,6 @@ func mapSlice[S, T any](slice []S, fn func(S) T) []T {
3115
return result
3216
}
3317

34-
// AwaitJS is a helper function equivalent to await in JS.
35-
// It is copied from https://go-review.googlesource.com/c/go/+/150917/
36-
func AwaitJS(promise js.Value) (result js.Value, ok bool) {
37-
if promise.Type() != js.TypeObject || promise.Get("then").Type() != js.TypeFunction {
38-
return promise, true
39-
}
40-
41-
done := make(chan struct{})
42-
43-
onResolve := js.FuncOf(func(this js.Value, args []js.Value) any {
44-
result = args[0]
45-
ok = true
46-
close(done)
47-
return nil
48-
})
49-
defer onResolve.Release()
50-
51-
onReject := js.FuncOf(func(this js.Value, args []js.Value) any {
52-
result = args[0]
53-
ok = false
54-
slog.Error("wgpu.AwaitJS: promise rejected", "reason", result)
55-
close(done)
56-
return nil
57-
})
58-
defer onReject.Release()
59-
60-
promise.Call("then", onResolve, onReject)
61-
<-done
62-
return
63-
}
64-
6518
// no-ops
6619
func SetLogLevel(level LogLevel) {}
6720
func GetVersion() Version { return 0 }

0 commit comments

Comments
 (0)