reze-engine 0.1.4 → 0.1.5

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/dist/engine.d.ts CHANGED
@@ -3,14 +3,6 @@ import { Quat, Vec3 } from "./math";
3
3
  export interface EngineStats {
4
4
  fps: number;
5
5
  frameTime: number;
6
- memoryUsed: number;
7
- vertices: number;
8
- drawCalls: number;
9
- triangles: number;
10
- materials: number;
11
- textures: number;
12
- textureMemory: number;
13
- bufferMemory: number;
14
6
  gpuMemory: number;
15
7
  }
16
8
  export declare class Engine {
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
package/dist/engine.js CHANGED
@@ -24,14 +24,6 @@ export class Engine {
24
24
  this.stats = {
25
25
  fps: 0,
26
26
  frameTime: 0,
27
- memoryUsed: 0,
28
- vertices: 0,
29
- drawCalls: 0,
30
- triangles: 0,
31
- materials: 0,
32
- textures: 0,
33
- textureMemory: 0,
34
- bufferMemory: 0,
35
27
  gpuMemory: 0,
36
28
  };
37
29
  this.animationFrameId = null;
@@ -646,7 +638,11 @@ export class Engine {
646
638
  const texture = await loadTextureByIndex(toonTextureIndex);
647
639
  if (texture)
648
640
  return texture;
649
- // Default toon texture fallback
641
+ // Default toon texture fallback - cache it
642
+ const defaultToonPath = "__default_toon__";
643
+ const cached = this.textureCache.get(defaultToonPath);
644
+ if (cached)
645
+ return cached;
650
646
  const defaultToonData = new Uint8Array(256 * 2 * 4);
651
647
  for (let i = 0; i < 256; i++) {
652
648
  const factor = i / 255.0;
@@ -667,7 +663,8 @@ export class Engine {
667
663
  usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
668
664
  });
669
665
  this.device.queue.writeTexture({ texture: defaultToonTexture }, defaultToonData, { bytesPerRow: 256 * 4 }, [256, 2]);
670
- this.textureSizes.set("__default_toon__", { width: 256, height: 2 });
666
+ this.textureCache.set(defaultToonPath, defaultToonTexture);
667
+ this.textureSizes.set(defaultToonPath, { width: 256, height: 2 });
671
668
  return defaultToonTexture;
672
669
  };
673
670
  this.materialDraws = [];
@@ -752,8 +749,8 @@ export class Engine {
752
749
  runningFirstIndex += matCount;
753
750
  }
754
751
  }
755
- // Helper: Load texture from file path
756
- async createTextureFromPath(path) {
752
+ // Helper: Load texture from file path with optional max size limit
753
+ async createTextureFromPath(path, maxSize = 2048) {
757
754
  const cached = this.textureCache.get(path);
758
755
  if (cached) {
759
756
  return cached;
@@ -763,22 +760,34 @@ export class Engine {
763
760
  if (!response.ok) {
764
761
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
765
762
  }
766
- const imageBitmap = await createImageBitmap(await response.blob(), {
763
+ let imageBitmap = await createImageBitmap(await response.blob(), {
767
764
  premultiplyAlpha: "none",
768
765
  colorSpaceConversion: "none",
769
766
  });
767
+ // Downscale if texture is too large
768
+ let finalWidth = imageBitmap.width;
769
+ let finalHeight = imageBitmap.height;
770
+ if (finalWidth > maxSize || finalHeight > maxSize) {
771
+ const scale = Math.min(maxSize / finalWidth, maxSize / finalHeight);
772
+ finalWidth = Math.floor(finalWidth * scale);
773
+ finalHeight = Math.floor(finalHeight * scale);
774
+ // Create canvas to downscale
775
+ const canvas = new OffscreenCanvas(finalWidth, finalHeight);
776
+ const ctx = canvas.getContext("2d");
777
+ if (ctx) {
778
+ ctx.drawImage(imageBitmap, 0, 0, finalWidth, finalHeight);
779
+ imageBitmap = await createImageBitmap(canvas);
780
+ }
781
+ }
770
782
  const texture = this.device.createTexture({
771
783
  label: `texture: ${path}`,
772
- size: [imageBitmap.width, imageBitmap.height],
784
+ size: [finalWidth, finalHeight],
773
785
  format: "rgba8unorm",
774
786
  usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
775
787
  });
776
- this.device.queue.copyExternalImageToTexture({ source: imageBitmap }, { texture }, [
777
- imageBitmap.width,
778
- imageBitmap.height,
779
- ]);
788
+ this.device.queue.copyExternalImageToTexture({ source: imageBitmap }, { texture }, [finalWidth, finalHeight]);
780
789
  this.textureCache.set(path, texture);
781
- this.textureSizes.set(path, { width: imageBitmap.width, height: imageBitmap.height });
790
+ this.textureSizes.set(path, { width: finalWidth, height: finalHeight });
782
791
  return texture;
783
792
  }
784
793
  catch {
@@ -914,41 +923,14 @@ export class Engine {
914
923
  this.stats.fps = Math.round((this.framesSinceLastUpdate / elapsed) * 1000);
915
924
  this.framesSinceLastUpdate = 0;
916
925
  this.lastFpsUpdate = now;
917
- const perf = performance;
918
- if (perf.memory) {
919
- this.stats.memoryUsed = Math.round(perf.memory.usedJSHeapSize / 1024 / 1024);
920
- }
921
- }
922
- this.stats.vertices = this.vertexCount;
923
- this.stats.drawCalls = this.drawCallCount;
924
- // Calculate triangles from index buffer
925
- if (this.indexBuffer) {
926
- const indexCount = this.currentModel?.getIndices()?.length || 0;
927
- this.stats.triangles = Math.floor(indexCount / 3);
928
- }
929
- else {
930
- this.stats.triangles = Math.floor(this.vertexCount / 3);
931
926
  }
932
- // Material count
933
- this.stats.materials = this.materialDraws.length;
934
- // Texture stats
935
- this.stats.textures = this.textureCache.size;
927
+ // Calculate GPU memory: textures + buffers + render targets
936
928
  let textureMemoryBytes = 0;
937
929
  for (const [path, size] of this.textureSizes.entries()) {
938
930
  if (this.textureCache.has(path)) {
939
- // RGBA8 = 4 bytes per pixel
940
- textureMemoryBytes += size.width * size.height * 4;
931
+ textureMemoryBytes += size.width * size.height * 4; // RGBA8 = 4 bytes per pixel
941
932
  }
942
933
  }
943
- // Add render target textures (multisample + depth)
944
- if (this.multisampleTexture) {
945
- const width = this.canvas.width;
946
- const height = this.canvas.height;
947
- textureMemoryBytes += width * height * 4 * this.sampleCount; // multisample color
948
- textureMemoryBytes += width * height * 4; // depth (depth24plus = 4 bytes)
949
- }
950
- this.stats.textureMemory = Math.round((textureMemoryBytes / 1024 / 1024) * 100) / 100;
951
- // Buffer memory estimate
952
934
  let bufferMemoryBytes = 0;
953
935
  if (this.vertexBuffer) {
954
936
  const vertices = this.currentModel?.getVertices();
@@ -977,10 +959,15 @@ export class Engine {
977
959
  }
978
960
  bufferMemoryBytes += 40 * 4; // cameraUniformBuffer
979
961
  bufferMemoryBytes += 64 * 4; // lightUniformBuffer
980
- // Material uniform buffers (estimate: 4 bytes per material)
981
- bufferMemoryBytes += this.materialDraws.length * 4;
982
- this.stats.bufferMemory = Math.round((bufferMemoryBytes / 1024 / 1024) * 100) / 100;
983
- // Total GPU memory estimate
984
- this.stats.gpuMemory = Math.round((this.stats.textureMemory + this.stats.bufferMemory) * 100) / 100;
962
+ bufferMemoryBytes += this.materialDraws.length * 4; // Material uniform buffers
963
+ let renderTargetMemoryBytes = 0;
964
+ if (this.multisampleTexture) {
965
+ const width = this.canvas.width;
966
+ const height = this.canvas.height;
967
+ renderTargetMemoryBytes += width * height * 4 * this.sampleCount; // multisample color
968
+ renderTargetMemoryBytes += width * height * 4; // depth
969
+ }
970
+ const totalGPUMemoryBytes = textureMemoryBytes + bufferMemoryBytes + renderTargetMemoryBytes;
971
+ this.stats.gpuMemory = Math.round((totalGPUMemoryBytes / 1024 / 1024) * 100) / 100;
985
972
  }
986
973
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reze-engine",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "A WebGPU-based MMD model renderer",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/src/engine.ts CHANGED
@@ -7,14 +7,6 @@ import { Physics } from "./physics"
7
7
  export interface EngineStats {
8
8
  fps: number
9
9
  frameTime: number // ms
10
- memoryUsed: number // MB (JS heap)
11
- vertices: number
12
- drawCalls: number
13
- triangles: number
14
- materials: number
15
- textures: number
16
- textureMemory: number // MB
17
- bufferMemory: number // MB
18
10
  gpuMemory: number // MB (estimated total GPU memory)
19
11
  }
20
12
 
@@ -62,14 +54,6 @@ export class Engine {
62
54
  private stats: EngineStats = {
63
55
  fps: 0,
64
56
  frameTime: 0,
65
- memoryUsed: 0,
66
- vertices: 0,
67
- drawCalls: 0,
68
- triangles: 0,
69
- materials: 0,
70
- textures: 0,
71
- textureMemory: 0,
72
- bufferMemory: 0,
73
57
  gpuMemory: 0,
74
58
  }
75
59
  private animationFrameId: number | null = null
@@ -764,7 +748,11 @@ export class Engine {
764
748
  const texture = await loadTextureByIndex(toonTextureIndex)
765
749
  if (texture) return texture
766
750
 
767
- // Default toon texture fallback
751
+ // Default toon texture fallback - cache it
752
+ const defaultToonPath = "__default_toon__"
753
+ const cached = this.textureCache.get(defaultToonPath)
754
+ if (cached) return cached
755
+
768
756
  const defaultToonData = new Uint8Array(256 * 2 * 4)
769
757
  for (let i = 0; i < 256; i++) {
770
758
  const factor = i / 255.0
@@ -790,7 +778,8 @@ export class Engine {
790
778
  { bytesPerRow: 256 * 4 },
791
779
  [256, 2]
792
780
  )
793
- this.textureSizes.set("__default_toon__", { width: 256, height: 2 })
781
+ this.textureCache.set(defaultToonPath, defaultToonTexture)
782
+ this.textureSizes.set(defaultToonPath, { width: 256, height: 2 })
794
783
  return defaultToonTexture
795
784
  }
796
785
 
@@ -888,8 +877,8 @@ export class Engine {
888
877
  }
889
878
  }
890
879
 
891
- // Helper: Load texture from file path
892
- private async createTextureFromPath(path: string): Promise<GPUTexture | null> {
880
+ // Helper: Load texture from file path with optional max size limit
881
+ private async createTextureFromPath(path: string, maxSize: number = 2048): Promise<GPUTexture | null> {
893
882
  const cached = this.textureCache.get(path)
894
883
  if (cached) {
895
884
  return cached
@@ -900,23 +889,38 @@ export class Engine {
900
889
  if (!response.ok) {
901
890
  throw new Error(`HTTP ${response.status}: ${response.statusText}`)
902
891
  }
903
- const imageBitmap = await createImageBitmap(await response.blob(), {
892
+ let imageBitmap = await createImageBitmap(await response.blob(), {
904
893
  premultiplyAlpha: "none",
905
894
  colorSpaceConversion: "none",
906
895
  })
896
+
897
+ // Downscale if texture is too large
898
+ let finalWidth = imageBitmap.width
899
+ let finalHeight = imageBitmap.height
900
+ if (finalWidth > maxSize || finalHeight > maxSize) {
901
+ const scale = Math.min(maxSize / finalWidth, maxSize / finalHeight)
902
+ finalWidth = Math.floor(finalWidth * scale)
903
+ finalHeight = Math.floor(finalHeight * scale)
904
+
905
+ // Create canvas to downscale
906
+ const canvas = new OffscreenCanvas(finalWidth, finalHeight)
907
+ const ctx = canvas.getContext("2d")
908
+ if (ctx) {
909
+ ctx.drawImage(imageBitmap, 0, 0, finalWidth, finalHeight)
910
+ imageBitmap = await createImageBitmap(canvas)
911
+ }
912
+ }
913
+
907
914
  const texture = this.device.createTexture({
908
915
  label: `texture: ${path}`,
909
- size: [imageBitmap.width, imageBitmap.height],
916
+ size: [finalWidth, finalHeight],
910
917
  format: "rgba8unorm",
911
918
  usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
912
919
  })
913
- this.device.queue.copyExternalImageToTexture({ source: imageBitmap }, { texture }, [
914
- imageBitmap.width,
915
- imageBitmap.height,
916
- ])
920
+ this.device.queue.copyExternalImageToTexture({ source: imageBitmap }, { texture }, [finalWidth, finalHeight])
917
921
 
918
922
  this.textureCache.set(path, texture)
919
- this.textureSizes.set(path, { width: imageBitmap.width, height: imageBitmap.height })
923
+ this.textureSizes.set(path, { width: finalWidth, height: finalHeight })
920
924
  return texture
921
925
  } catch {
922
926
  return null
@@ -1083,48 +1087,16 @@ export class Engine {
1083
1087
  this.stats.fps = Math.round((this.framesSinceLastUpdate / elapsed) * 1000)
1084
1088
  this.framesSinceLastUpdate = 0
1085
1089
  this.lastFpsUpdate = now
1086
-
1087
- const perf = performance as Performance & {
1088
- memory?: { usedJSHeapSize: number; totalJSHeapSize: number }
1089
- }
1090
- if (perf.memory) {
1091
- this.stats.memoryUsed = Math.round(perf.memory.usedJSHeapSize / 1024 / 1024)
1092
- }
1093
1090
  }
1094
1091
 
1095
- this.stats.vertices = this.vertexCount
1096
- this.stats.drawCalls = this.drawCallCount
1097
-
1098
- // Calculate triangles from index buffer
1099
- if (this.indexBuffer) {
1100
- const indexCount = this.currentModel?.getIndices()?.length || 0
1101
- this.stats.triangles = Math.floor(indexCount / 3)
1102
- } else {
1103
- this.stats.triangles = Math.floor(this.vertexCount / 3)
1104
- }
1105
-
1106
- // Material count
1107
- this.stats.materials = this.materialDraws.length
1108
-
1109
- // Texture stats
1110
- this.stats.textures = this.textureCache.size
1092
+ // Calculate GPU memory: textures + buffers + render targets
1111
1093
  let textureMemoryBytes = 0
1112
1094
  for (const [path, size] of this.textureSizes.entries()) {
1113
1095
  if (this.textureCache.has(path)) {
1114
- // RGBA8 = 4 bytes per pixel
1115
- textureMemoryBytes += size.width * size.height * 4
1096
+ textureMemoryBytes += size.width * size.height * 4 // RGBA8 = 4 bytes per pixel
1116
1097
  }
1117
1098
  }
1118
- // Add render target textures (multisample + depth)
1119
- if (this.multisampleTexture) {
1120
- const width = this.canvas.width
1121
- const height = this.canvas.height
1122
- textureMemoryBytes += width * height * 4 * this.sampleCount // multisample color
1123
- textureMemoryBytes += width * height * 4 // depth (depth24plus = 4 bytes)
1124
- }
1125
- this.stats.textureMemory = Math.round((textureMemoryBytes / 1024 / 1024) * 100) / 100
1126
1099
 
1127
- // Buffer memory estimate
1128
1100
  let bufferMemoryBytes = 0
1129
1101
  if (this.vertexBuffer) {
1130
1102
  const vertices = this.currentModel?.getVertices()
@@ -1148,11 +1120,17 @@ export class Engine {
1148
1120
  }
1149
1121
  bufferMemoryBytes += 40 * 4 // cameraUniformBuffer
1150
1122
  bufferMemoryBytes += 64 * 4 // lightUniformBuffer
1151
- // Material uniform buffers (estimate: 4 bytes per material)
1152
- bufferMemoryBytes += this.materialDraws.length * 4
1153
- this.stats.bufferMemory = Math.round((bufferMemoryBytes / 1024 / 1024) * 100) / 100
1123
+ bufferMemoryBytes += this.materialDraws.length * 4 // Material uniform buffers
1124
+
1125
+ let renderTargetMemoryBytes = 0
1126
+ if (this.multisampleTexture) {
1127
+ const width = this.canvas.width
1128
+ const height = this.canvas.height
1129
+ renderTargetMemoryBytes += width * height * 4 * this.sampleCount // multisample color
1130
+ renderTargetMemoryBytes += width * height * 4 // depth
1131
+ }
1154
1132
 
1155
- // Total GPU memory estimate
1156
- this.stats.gpuMemory = Math.round((this.stats.textureMemory + this.stats.bufferMemory) * 100) / 100
1133
+ const totalGPUMemoryBytes = textureMemoryBytes + bufferMemoryBytes + renderTargetMemoryBytes
1134
+ this.stats.gpuMemory = Math.round((totalGPUMemoryBytes / 1024 / 1024) * 100) / 100
1157
1135
  }
1158
1136
  }