@mrigx/core 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.
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@mrigx/core",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/core.umd.js",
6
+ "module": "./dist/core.es.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/core.es.js",
12
+ "require": "./dist/core.umd.js"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "vite build"
17
+ },
18
+ "dependencies": {
19
+ "html-to-image": "^1.11.13"
20
+ },
21
+ "devDependencies": {
22
+ "typescript": "~5.7.2",
23
+ "vite": "^6.0.0",
24
+ "vite-plugin-dts": "^4.5.0"
25
+ }
26
+ }
@@ -0,0 +1,125 @@
1
+ import { MrigEngine } from './MrigEngine';
2
+
3
+ export interface AtlasRegion {
4
+ id: string;
5
+ x: number;
6
+ y: number;
7
+ width: number;
8
+ height: number;
9
+ uv: [number, number, number, number]; // [uMin, vMin, uMax, vMax]
10
+ }
11
+
12
+ /**
13
+ * Manages a large WebGPU texture atlas for dynamically caching UI components.
14
+ * This allows the GPU to instantly translate components without re-rasterizing the DOM.
15
+ */
16
+ export class AtlasManager {
17
+ private engine: MrigEngine;
18
+ public texture: GPUTexture | null = null;
19
+ public textureSize = 4096; // 4K atlas holds a LOT of UI components
20
+
21
+ private regions = new Map<string, AtlasRegion>();
22
+
23
+ // Simple Shelf Packing State
24
+ private currentX = 0;
25
+ private currentY = 0;
26
+ private shelfHeight = 0;
27
+
28
+ constructor(engine: MrigEngine) {
29
+ this.engine = engine;
30
+ }
31
+
32
+ public init() {
33
+ const device = this.engine.gpuManager.device;
34
+ if (!device) return;
35
+
36
+ this.texture = device.createTexture({
37
+ size: [this.textureSize, this.textureSize, 1],
38
+ format: 'rgba8unorm',
39
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
40
+ });
41
+ }
42
+
43
+ /**
44
+ * Patches a rasterized component directly into a sub-region of the atlas texture.
45
+ * If the component is new or changed size, it dynamically allocates a new shelf slot.
46
+ */
47
+ public uploadSprite(id: string, sourceCanvas: HTMLCanvasElement | ImageBitmap): AtlasRegion | null {
48
+ if (!this.texture || !this.engine.gpuManager.device) return null;
49
+
50
+ const width = sourceCanvas.width;
51
+ const height = sourceCanvas.height;
52
+
53
+ let region: AtlasRegion | null = this.regions.get(id) || null;
54
+
55
+ // If it's a new sprite or the dimensions changed, we must allocate a new slot
56
+ if (!region || region.width !== width || region.height !== height) {
57
+ region = this.allocate(id, width, height);
58
+ if (!region) {
59
+ console.warn('Mrig Atlas is full! Cannot allocate sprite.');
60
+ return null;
61
+ }
62
+ }
63
+
64
+ // Zero-overhead GPU memory patch. Replaces ONLY the pixels of this specific component.
65
+ this.engine.gpuManager.device.queue.copyExternalImageToTexture(
66
+ { source: sourceCanvas },
67
+ { texture: this.texture, origin: [region.x, region.y, 0] },
68
+ [width, height, 1]
69
+ );
70
+
71
+ return region;
72
+ }
73
+
74
+ /**
75
+ * Standard 2D shelf-packing algorithm to tightly pack UI sprites into the atlas.
76
+ */
77
+ private allocate(id: string, width: number, height: number): AtlasRegion | null {
78
+ // If it doesn't fit on the current shelf, move to the next shelf down
79
+ if (this.currentX + width > this.textureSize) {
80
+ this.currentX = 0;
81
+ this.currentY += this.shelfHeight;
82
+ this.shelfHeight = 0;
83
+ }
84
+
85
+ // If it still doesn't fit vertically, the atlas is full
86
+ if (this.currentY + height > this.textureSize) {
87
+ return null;
88
+ }
89
+
90
+ const region: AtlasRegion = {
91
+ id,
92
+ x: this.currentX,
93
+ y: this.currentY,
94
+ width,
95
+ height,
96
+ uv: [
97
+ this.currentX / this.textureSize,
98
+ this.currentY / this.textureSize,
99
+ (this.currentX + width) / this.textureSize,
100
+ (this.currentY + height) / this.textureSize
101
+ ]
102
+ };
103
+
104
+ // Expand the shelf height if this sprite is taller
105
+ this.shelfHeight = Math.max(this.shelfHeight, height);
106
+ // Move the cursor right
107
+ this.currentX += width;
108
+
109
+ this.regions.set(id, region);
110
+ return region;
111
+ }
112
+
113
+ public getRegion(id: string): AtlasRegion | undefined {
114
+ return this.regions.get(id);
115
+ }
116
+
117
+ public destroy() {
118
+ if (this.texture) {
119
+ this.texture.destroy();
120
+ this.texture = null;
121
+ }
122
+ this.regions.clear();
123
+ }
124
+ }
125
+
@@ -0,0 +1,113 @@
1
+ /**
2
+ * @file DomRasterSystem.ts
3
+ * @description Rasterizes the DOM into a WebGPU texture to allow the optical engine to distort the native HTML contents.
4
+ * Instrumented with profiling to diagnose main-thread bottlenecks.
5
+ */
6
+
7
+ import { toCanvas } from 'html-to-image';
8
+ import { MrigEngine } from './MrigEngine';
9
+
10
+ export class DomRasterSystem {
11
+ private engine: MrigEngine;
12
+ private targetEl: HTMLElement | null = null;
13
+ private isRasterizing = false;
14
+ private pendingRasterize = false;
15
+ private listeners: (() => void)[] = [];
16
+
17
+ public onRasterizeComplete(cb: () => void) {
18
+ this.listeners.push(cb);
19
+ return () => { this.listeners = this.listeners.filter(l => l !== cb); };
20
+ }
21
+
22
+ constructor(engine: MrigEngine) {
23
+ this.engine = engine;
24
+ }
25
+
26
+ public setTarget(el: HTMLElement) {
27
+ this.targetEl = el;
28
+ this.rasterize();
29
+ }
30
+
31
+ public async rasterize() {
32
+ if (!this.targetEl || !this.engine.gpuManager.device) return;
33
+
34
+ if (this.isRasterizing) {
35
+ this.pendingRasterize = true;
36
+ return;
37
+ }
38
+
39
+ this.isRasterizing = true;
40
+ this.pendingRasterize = false;
41
+
42
+ try {
43
+ const dpr = window.devicePixelRatio || 1;
44
+ const targetWidth = document.documentElement.scrollWidth || window.innerWidth;
45
+ const targetHeight = document.documentElement.scrollHeight || window.innerHeight;
46
+
47
+ const startCanvas = performance.now();
48
+ const canvas = await toCanvas(this.targetEl, {
49
+ backgroundColor: 'transparent',
50
+ width: targetWidth,
51
+ height: targetHeight,
52
+ pixelRatio: dpr,
53
+ style: {
54
+ opacity: '1',
55
+ pointerEvents: 'none',
56
+ '--theme-color': getComputedStyle(document.documentElement).getPropertyValue('--theme-color').trim() || '#ffffff',
57
+ '--color-white': getComputedStyle(document.documentElement).getPropertyValue('--theme-color').trim() || '#ffffff',
58
+ },
59
+ skipFonts: true,
60
+ filter: (node) => {
61
+ if (node instanceof HTMLElement && node.id === 'mrig-transition-overlay') return false;
62
+ return true;
63
+ },
64
+ });
65
+ const durationCanvas = performance.now() - startCanvas;
66
+
67
+ const cWidth = canvas.width;
68
+ const cHeight = canvas.height;
69
+ const pixelCount = cWidth * cHeight;
70
+
71
+ const startTexture = performance.now();
72
+ const newTexture = this.engine.gpuManager.device.createTexture({
73
+ size: [cWidth, cHeight, 1],
74
+ format: 'rgba8unorm',
75
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
76
+ });
77
+ const durationTexture = performance.now() - startTexture;
78
+
79
+ const startUpload = performance.now();
80
+ this.engine.gpuManager.device.queue.copyExternalImageToTexture(
81
+ { source: canvas },
82
+ { texture: newTexture },
83
+ [cWidth, cHeight, 1]
84
+ );
85
+ const durationUpload = performance.now() - startUpload;
86
+
87
+ console.log(
88
+ '[Mrig Profiler] Full Page Snapshot\n' +
89
+ '- Dimensions: ' + cWidth + 'x' + cHeight + ' (' + (pixelCount / 1000000).toFixed(2) + 'M pixels)\n' +
90
+ '- toCanvas: ' + durationCanvas.toFixed(2) + 'ms\n' +
91
+ '- createTexture: ' + durationTexture.toFixed(2) + 'ms\n' +
92
+ '- uploadTexture: ' + durationUpload.toFixed(2) + 'ms\n' +
93
+ '- Total time: ' + (durationCanvas + durationTexture + durationUpload).toFixed(2) + 'ms'
94
+ );
95
+
96
+ this.engine.updateDomTexture(newTexture);
97
+
98
+ } catch (e) {
99
+ console.error('Mrig DOM Rasterization failed:', e);
100
+ }
101
+
102
+ this.isRasterizing = false;
103
+ this.listeners.forEach(cb => cb());
104
+ if (typeof window !== 'undefined') {
105
+ window.dispatchEvent(new CustomEvent('mrig-raster-complete'));
106
+ }
107
+
108
+ if (this.pendingRasterize) {
109
+ setTimeout(() => this.rasterize(), 33);
110
+ }
111
+ }
112
+ }
113
+