@mudah-cli/vgpu 0.4.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,2 @@
1
+ export { FramePresenter, type FramePresenterOptions, type PresentMode, } from './presenter.js';
2
+ export { ShaderSession, type GpuAdapterKind, type ShaderSessionOptions, } from './session.js';
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { FramePresenter, } from './presenter.js';
2
+ export { ShaderSession, } from './session.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,aAAa,GAGd,MAAM,cAAc,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { type OscWriter, type PixelFormat, type TerminalCapabilities } from '@mudah-cli/terminal';
2
+ export type PresentMode = 'auto' | 'kitty' | 'half';
3
+ export interface FramePresenterOptions {
4
+ stdout?: OscWriter;
5
+ capabilities?: TerminalCapabilities;
6
+ /** Default `auto`: Kitty graphics when the terminal speaks it. */
7
+ mode?: PresentMode;
8
+ format?: PixelFormat;
9
+ /** Image id reused every frame so the placement replaces in place. */
10
+ id?: number;
11
+ }
12
+ /**
13
+ * Blit an RGBA/RGB framebuffer to the terminal. Prefers the Kitty graphics
14
+ * protocol; falls back to unicode half-blocks on terminals that cannot
15
+ * place images.
16
+ */
17
+ export declare class FramePresenter {
18
+ private readonly writer;
19
+ readonly mode: Exclude<PresentMode, 'auto'>;
20
+ private readonly format;
21
+ private readonly gfx;
22
+ constructor(options?: FramePresenterOptions);
23
+ /**
24
+ * Draw `pixels` at the current cursor. For Kitty mode, pass `columns` /
25
+ * `rows` to scale the image into the cell grid.
26
+ */
27
+ present(pixels: Uint8Array, width: number, height: number, layout?: {
28
+ columns?: number;
29
+ rows?: number;
30
+ }): void;
31
+ clear(): void;
32
+ }
@@ -0,0 +1,43 @@
1
+ import { KittyGraphics, detectCapabilities, encodeHalfBlocks, } from '@mudah-cli/terminal';
2
+ /**
3
+ * Blit an RGBA/RGB framebuffer to the terminal. Prefers the Kitty graphics
4
+ * protocol; falls back to unicode half-blocks on terminals that cannot
5
+ * place images.
6
+ */
7
+ export class FramePresenter {
8
+ writer;
9
+ mode;
10
+ format;
11
+ gfx;
12
+ constructor(options = {}) {
13
+ this.writer = options.stdout ?? process.stdout;
14
+ const caps = options.capabilities ?? detectCapabilities();
15
+ const requested = options.mode ?? 'auto';
16
+ this.mode =
17
+ requested === 'auto' ? (caps.kittyGraphics ? 'kitty' : 'half') : requested;
18
+ this.format = options.format ?? 'rgba';
19
+ this.gfx = new KittyGraphics(this.writer, { id: options.id ?? 1 });
20
+ }
21
+ /**
22
+ * Draw `pixels` at the current cursor. For Kitty mode, pass `columns` /
23
+ * `rows` to scale the image into the cell grid.
24
+ */
25
+ present(pixels, width, height, layout = {}) {
26
+ if (this.mode === 'kitty') {
27
+ this.gfx.draw(pixels, width, height, {
28
+ format: this.format,
29
+ columns: layout.columns,
30
+ rows: layout.rows,
31
+ });
32
+ return;
33
+ }
34
+ const lines = encodeHalfBlocks(pixels, width, height, this.format);
35
+ this.writer.write(lines.join('\n'));
36
+ this.writer.write('\n');
37
+ }
38
+ clear() {
39
+ if (this.mode === 'kitty')
40
+ this.gfx.delete(true);
41
+ }
42
+ }
43
+ //# sourceMappingURL=presenter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presenter.js","sourceRoot":"","sources":["../src/presenter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,gBAAgB,GAIjB,MAAM,qBAAqB,CAAC;AAc7B;;;;GAIG;AACH,MAAM,OAAO,cAAc;IACR,MAAM,CAAY;IAC1B,IAAI,CAA+B;IAC3B,MAAM,CAAc;IACpB,GAAG,CAAgB;IAEpC,YAAY,OAAO,GAA0B,EAAE;QAC7C,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;QAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,IAAI,kBAAkB,EAAE,CAAC;QAC1D,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC;QACzC,IAAI,CAAC,IAAI;YACP,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7E,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC;QACvC,IAAI,CAAC,GAAG,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IACrE,CAAC;IAED;;;OAGG;IACH,OAAO,CACL,MAAkB,EAClB,KAAa,EACb,MAAc,EACd,MAAM,GAAwC,EAAE;QAEhD,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;gBACnC,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAI;aAClB,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;YAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnD,CAAC;CACF"}
@@ -0,0 +1,48 @@
1
+ import type { OscWriter } from '@mudah-cli/terminal';
2
+ import { FramePresenter, type PresentMode } from './presenter.js';
3
+ export type GpuAdapterKind = 'auto' | 'hardware' | 'software' | 'mock';
4
+ export interface ShaderSessionOptions {
5
+ /** Fragment-only WGSL. vgpu injects a fullscreen triangle and `uv`. */
6
+ shader: string;
7
+ width?: number;
8
+ height?: number;
9
+ /** Initial uniform / texture bindings, keyed by WGSL name. */
10
+ set?: Record<string, unknown>;
11
+ adapter?: GpuAdapterKind;
12
+ stdout?: OscWriter;
13
+ present?: PresentMode;
14
+ label?: string;
15
+ }
16
+ /**
17
+ * A WGSL effect that renders offscreen through vgpu, then blits the pixels
18
+ * to the terminal. Optional: apps that never import `@mudah-cli/vgpu` never
19
+ * load Dawn or vgpu.
20
+ */
21
+ export declare class ShaderSession {
22
+ readonly width: number;
23
+ readonly height: number;
24
+ readonly presenter: FramePresenter;
25
+ private readonly gpu;
26
+ private readonly vgpu;
27
+ private effect;
28
+ private readonly target;
29
+ private lastFrame;
30
+ private constructor();
31
+ static create(options: ShaderSessionOptions): Promise<ShaderSession>;
32
+ /** Replace the fragment shader without tearing down the GPU device. */
33
+ useShader(source: string, set?: Record<string, unknown>, label?: string): this;
34
+ set(values: Record<string, unknown>): this;
35
+ /** Draw one frame and return tightly packed RGBA bytes. */
36
+ render(): Promise<Uint8Array>;
37
+ /** Draw, then blit to the terminal. */
38
+ frame(layout?: {
39
+ columns?: number;
40
+ rows?: number;
41
+ }): Promise<Uint8Array>;
42
+ /** Re-blit the last rendered frame without another GPU pass. */
43
+ presentLast(layout?: {
44
+ columns?: number;
45
+ rows?: number;
46
+ }): void;
47
+ dispose(): void;
48
+ }
@@ -0,0 +1,103 @@
1
+ import { FramePresenter } from './presenter.js';
2
+ async function loadVgpu(adapter) {
3
+ try {
4
+ if (adapter === 'mock') {
5
+ return (await import('vgpu/mock'));
6
+ }
7
+ return (await import('vgpu/node'));
8
+ }
9
+ catch (error) {
10
+ const reason = error instanceof Error ? error.message : String(error);
11
+ throw new Error(`[vgpu] Failed to load the vgpu ${adapter === 'mock' ? 'mock' : 'Node'} adapter (${reason}). Install the vgpu package and, on a machine with no GPU, run \`npx vgpu install-software-renderer\`.`);
12
+ }
13
+ }
14
+ /**
15
+ * A WGSL effect that renders offscreen through vgpu, then blits the pixels
16
+ * to the terminal. Optional: apps that never import `@mudah-cli/vgpu` never
17
+ * load Dawn or vgpu.
18
+ */
19
+ export class ShaderSession {
20
+ width;
21
+ height;
22
+ presenter;
23
+ gpu;
24
+ vgpu;
25
+ effect;
26
+ target;
27
+ lastFrame;
28
+ constructor(vgpu, gpu, effect, target, presenter, width, height) {
29
+ this.vgpu = vgpu;
30
+ this.gpu = gpu;
31
+ this.effect = effect;
32
+ this.target = target;
33
+ this.presenter = presenter;
34
+ this.width = width;
35
+ this.height = height;
36
+ }
37
+ static async create(options) {
38
+ const width = options.width ?? 320;
39
+ const height = options.height ?? 180;
40
+ const adapter = options.adapter ?? 'auto';
41
+ const vgpu = await loadVgpu(adapter);
42
+ const initOpts = adapter === 'mock' || adapter === 'auto' ? {} : { adapter };
43
+ const gpu = await vgpu.init(initOpts);
44
+ const colorTarget = vgpu.target(gpu, { size: [width, height], format: 'rgba8unorm' });
45
+ const fx = vgpu.effect(gpu, options.shader, {
46
+ label: options.label ?? 'mudah-vgpu',
47
+ set: options.set,
48
+ });
49
+ const presenter = new FramePresenter({
50
+ stdout: options.stdout,
51
+ mode: options.present,
52
+ });
53
+ return new ShaderSession(vgpu, gpu, fx, colorTarget, presenter, width, height);
54
+ }
55
+ /** Replace the fragment shader without tearing down the GPU device. */
56
+ useShader(source, set, label) {
57
+ this.effect = this.vgpu.effect(this.gpu, source, {
58
+ label: label ?? 'mudah-vgpu',
59
+ set,
60
+ });
61
+ return this;
62
+ }
63
+ set(values) {
64
+ this.effect.set(values);
65
+ return this;
66
+ }
67
+ /** Draw one frame and return tightly packed RGBA bytes. */
68
+ async render() {
69
+ const errors = [];
70
+ const off = this.gpu.onError?.((error) => {
71
+ errors.push(error);
72
+ });
73
+ try {
74
+ this.effect.draw(this.target);
75
+ if (this.gpu.settled !== undefined)
76
+ await this.gpu.settled();
77
+ if (errors[0] !== undefined)
78
+ throw errors[0];
79
+ this.lastFrame = await this.target.read();
80
+ return this.lastFrame;
81
+ }
82
+ finally {
83
+ off?.();
84
+ }
85
+ }
86
+ /** Draw, then blit to the terminal. */
87
+ async frame(layout = {}) {
88
+ const pixels = await this.render();
89
+ this.presenter.present(pixels, this.width, this.height, layout);
90
+ return pixels;
91
+ }
92
+ /** Re-blit the last rendered frame without another GPU pass. */
93
+ presentLast(layout = {}) {
94
+ if (this.lastFrame === undefined)
95
+ return;
96
+ this.presenter.present(this.lastFrame, this.width, this.height, layout);
97
+ }
98
+ dispose() {
99
+ this.presenter.clear();
100
+ this.gpu.dispose();
101
+ }
102
+ }
103
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@mudah-cli/vgpu",
3
+ "version": "0.4.0",
4
+ "description": "Run vgpu WGSL effects inside a Mudah app and blit the pixels to the terminal (Kitty graphics, with a half-block fallback).",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "engines": {
8
+ "node": ">=26"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "sideEffects": false,
20
+ "dependencies": {
21
+ "@mudah-cli/terminal": "^0.4.0",
22
+ "vgpu": "^0.3.1"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ }
27
+ }