@aethervtt/shard-gpu 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,110 @@
1
+ class Surface {
2
+ label;
3
+ canvas;
4
+ alpha;
5
+ context;
6
+ /** Cameras showing on a surface render at the RenderScale and are upscaled (0051). */
7
+ renderScale = true;
8
+ /** Canvas pixels per CSS pixel (devicePixelRatio) at the last resize. */
9
+ pixelRatio = 1;
10
+ /** The device it's configured for. */
11
+ gpu;
12
+ observer;
13
+ resizeListeners = /* @__PURE__ */ new Set();
14
+ removed_ = false;
15
+ /** @internal Made by `GpuContext.addSurface`. */
16
+ constructor(gpu, canvas, context, options) {
17
+ this.gpu = gpu;
18
+ this.canvas = canvas;
19
+ this.context = context;
20
+ this.alpha = options.alpha;
21
+ this.label = options.label;
22
+ this.configure();
23
+ this.measure();
24
+ const Observer = globalThis.ResizeObserver;
25
+ if (Observer && isElement(canvas)) {
26
+ this.observer = new Observer((entries) => this.onObserved(entries));
27
+ try {
28
+ this.observer.observe(canvas, { box: "device-pixel-content-box" });
29
+ } catch {
30
+ this.observer.observe(canvas);
31
+ }
32
+ }
33
+ }
34
+ get format() {
35
+ return this.gpu.format;
36
+ }
37
+ get width() {
38
+ return this.canvas.width;
39
+ }
40
+ get height() {
41
+ return this.canvas.height;
42
+ }
43
+ /** True once `remove()` was called. */
44
+ get removed() {
45
+ return this.removed_;
46
+ }
47
+ /** The texture to render into this frame. */
48
+ texture() {
49
+ if (!this.observer) this.measure();
50
+ return this.context.getCurrentTexture();
51
+ }
52
+ /** Calls `listener` after the canvas's backing size changes. Returns an unsubscribe function. */
53
+ onResize(listener) {
54
+ this.resizeListeners.add(listener);
55
+ return () => this.resizeListeners.delete(listener);
56
+ }
57
+ /** Unconfigures the canvas, stops observing it, and takes it off the device. Idempotent. */
58
+ remove() {
59
+ if (this.removed_) return;
60
+ this.removed_ = true;
61
+ this.observer?.disconnect();
62
+ this.resizeListeners.clear();
63
+ this.context.unconfigure();
64
+ this.gpu.forgetSurface(this);
65
+ }
66
+ /** @internal Configures the canvas for the current device, keeping its alpha mode. */
67
+ configure() {
68
+ this.context.configure({
69
+ device: this.gpu.device,
70
+ format: this.gpu.format,
71
+ alphaMode: this.alpha,
72
+ // COPY_SRC lets the renderer capture screenshots straight from the swapchain.
73
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
74
+ });
75
+ }
76
+ /** Sizes the backing store from the element's CSS size (no-op for an OffscreenCanvas). */
77
+ measure() {
78
+ const canvas = this.canvas;
79
+ if (!isElement(canvas)) return;
80
+ const dpr = globalThis.devicePixelRatio ?? 1;
81
+ this.resize(canvas.clientWidth * dpr, canvas.clientHeight * dpr, dpr);
82
+ }
83
+ onObserved(entries) {
84
+ const entry = entries[entries.length - 1];
85
+ if (!entry || this.removed_) return;
86
+ const dpr = globalThis.devicePixelRatio ?? 1;
87
+ const box = entry.contentBoxSize?.[0];
88
+ const w = box ? box.inlineSize : entry.contentRect?.width;
89
+ const h = box ? box.blockSize : entry.contentRect?.height;
90
+ const exact = entry.devicePixelContentBoxSize?.[0];
91
+ if (exact) this.resize(exact.inlineSize, exact.blockSize, w ? exact.inlineSize / w : dpr);
92
+ else this.resize((w ?? 0) * dpr, (h ?? 0) * dpr, dpr);
93
+ }
94
+ resize(w, h, dpr) {
95
+ this.pixelRatio = dpr;
96
+ const width = Math.max(1, Math.floor(w));
97
+ const height = Math.max(1, Math.floor(h));
98
+ const canvas = this.canvas;
99
+ if (canvas.width === width && canvas.height === height) return;
100
+ canvas.width = width;
101
+ canvas.height = height;
102
+ for (const listener of this.resizeListeners) listener();
103
+ }
104
+ }
105
+ function isElement(canvas) {
106
+ return "clientWidth" in canvas;
107
+ }
108
+ export {
109
+ Surface
110
+ };
package/dist/tier.d.ts ADDED
@@ -0,0 +1,41 @@
1
+ export type GpuBackendName = 'webgpu' | 'webgl2';
2
+ export type GpuTier = 'full' | 'baseline';
3
+ /** What the device can do, as far as the tiers care. */
4
+ export interface GpuCapabilities {
5
+ /** A WebGPU core device (`core-features-and-limits`). */
6
+ core: boolean;
7
+ compute: boolean;
8
+ storageBuffersPerStage: {
9
+ vertex: number;
10
+ fragment: number;
11
+ compute: number;
12
+ };
13
+ /** Any format or dimension view of any texture (core only). */
14
+ arbitraryTextureViews: boolean;
15
+ maxTextureDimension2D: number;
16
+ maxColorAttachments: number;
17
+ maxUniformBufferBindingSize: number;
18
+ /**
19
+ * The most samples an HDR scene target (`rgba16float`) takes: 4, or 1 where float targets can't
20
+ * multisample (some compatibility-mode devices and WebGL2 implementations).
21
+ */
22
+ hdrSampleCount: 1 | 4;
23
+ }
24
+ /** Why a better option was skipped: `{ backend: 'webgpu', code: 'no-adapter', … }`. */
25
+ export interface GraphicsReason {
26
+ backend: GpuBackendName;
27
+ code: string;
28
+ message: string;
29
+ }
30
+ /**
31
+ * Whether a device follows core WebGPU's rules. Core devices carry `core-features-and-limits`;
32
+ * browsers from before compatibility mode know neither the feature nor its per-stage storage
33
+ * limits, and every device they make is core.
34
+ */
35
+ export declare function isCoreDevice(device: GPUDevice): boolean;
36
+ export declare function capabilitiesOf(device: GPUDevice, backend: GpuBackendName, hdrSampleCount?: 1 | 4): GpuCapabilities;
37
+ /**
38
+ * Whether `rgba16float` targets multisample 4×: always on a core device (checked without asking
39
+ * it anything), else found by creating one in an error scope.
40
+ */
41
+ export declare function probeHdrSampleCount(device: GPUDevice): Promise<1 | 4>;
package/dist/tier.js ADDED
@@ -0,0 +1,42 @@
1
+ function isCoreDevice(device) {
2
+ if (device.features.has("core-features-and-limits")) return true;
3
+ return device.limits.maxStorageBuffersInVertexStage === void 0;
4
+ }
5
+ function capabilitiesOf(device, backend, hdrSampleCount = 4) {
6
+ const limits = device.limits;
7
+ const core = backend === "webgpu" && isCoreDevice(device);
8
+ const perStage = limits.maxStorageBuffersPerShaderStage;
9
+ return {
10
+ core,
11
+ compute: backend === "webgpu",
12
+ storageBuffersPerStage: {
13
+ vertex: backend === "webgl2" ? 0 : limits.maxStorageBuffersInVertexStage ?? perStage,
14
+ fragment: backend === "webgl2" ? 0 : limits.maxStorageBuffersInFragmentStage ?? perStage,
15
+ compute: backend === "webgl2" ? 0 : perStage
16
+ },
17
+ arbitraryTextureViews: core,
18
+ maxTextureDimension2D: limits.maxTextureDimension2D,
19
+ maxColorAttachments: limits.maxColorAttachments,
20
+ maxUniformBufferBindingSize: limits.maxUniformBufferBindingSize,
21
+ hdrSampleCount
22
+ };
23
+ }
24
+ async function probeHdrSampleCount(device) {
25
+ if (isCoreDevice(device)) return 4;
26
+ device.pushErrorScope("validation");
27
+ const texture = device.createTexture({
28
+ label: "shard/msaa-probe",
29
+ size: [1, 1],
30
+ format: "rgba16float",
31
+ sampleCount: 4,
32
+ usage: GPUTextureUsage.RENDER_ATTACHMENT
33
+ });
34
+ const error = await device.popErrorScope();
35
+ texture.destroy();
36
+ return error ? 1 : 4;
37
+ }
38
+ export {
39
+ capabilitiesOf,
40
+ isCoreDevice,
41
+ probeHdrSampleCount
42
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aethervtt/shard-gpu",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -20,7 +20,8 @@
20
20
  "README.md"
21
21
  ],
22
22
  "dependencies": {
23
- "@aethervtt/shard-core": "0.0.1"
23
+ "@aethervtt/shard-core": "0.1.0",
24
+ "@aethervtt/shard-gpu-webgl2": "0.1.0"
24
25
  },
25
26
  "repository": {
26
27
  "type": "git",
@@ -32,7 +33,7 @@
32
33
  "provenance": true
33
34
  },
34
35
  "shard": {
35
- "buildSha": "d6c5c6aef4675575c878b02cf2c8f738b4190229"
36
+ "buildSha": "8e14c9896f82a3554b156f1ffdd68f2cc3e7d78a"
36
37
  },
37
38
  "peerDependencies": {
38
39
  "webgpu": "0.6.1"