bplinejs 0.0.101-beta.10

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.
Files changed (129) hide show
  1. package/README.md +215 -0
  2. package/docs/API.md +471 -0
  3. package/lib/Camera/index.d.ts +88 -0
  4. package/lib/Camera/index.js +178 -0
  5. package/lib/Camera/index.js.map +1 -0
  6. package/lib/Color/index.d.ts +99 -0
  7. package/lib/Color/index.js +178 -0
  8. package/lib/Color/index.js.map +1 -0
  9. package/lib/Geometry/Geo.d.ts +131 -0
  10. package/lib/Geometry/Geo.js +180 -0
  11. package/lib/Geometry/Geo.js.map +1 -0
  12. package/lib/Geometry/Poly2D.d.ts +53 -0
  13. package/lib/Geometry/Poly2D.js +133 -0
  14. package/lib/Geometry/Poly2D.js.map +1 -0
  15. package/lib/Geometry/Rect2d.d.ts +89 -0
  16. package/lib/Geometry/Rect2d.js +128 -0
  17. package/lib/Geometry/Rect2d.js.map +1 -0
  18. package/lib/Geometry/index.d.ts +6 -0
  19. package/lib/Geometry/index.js +4 -0
  20. package/lib/Group/index.d.ts +70 -0
  21. package/lib/Group/index.js +110 -0
  22. package/lib/Group/index.js.map +1 -0
  23. package/lib/ID.d.ts +6 -0
  24. package/lib/ID.js +15 -0
  25. package/lib/ID.js.map +1 -0
  26. package/lib/IMesh/index.d.ts +104 -0
  27. package/lib/IMesh/index.js +333 -0
  28. package/lib/IMesh/index.js.map +1 -0
  29. package/lib/Material/Material.d.ts +158 -0
  30. package/lib/Material/Material.js +229 -0
  31. package/lib/Material/Material.js.map +1 -0
  32. package/lib/Material/baseMaterial.d.ts +15 -0
  33. package/lib/Material/baseMaterial.js +24 -0
  34. package/lib/Material/baseMaterial.js.map +1 -0
  35. package/lib/Material/index.d.ts +4 -0
  36. package/lib/Material/index.js +3 -0
  37. package/lib/Material/shaders/base/poly/fragment.js +6 -0
  38. package/lib/Material/shaders/base/poly/fragment.js.map +1 -0
  39. package/lib/Material/shaders/base/poly/vertex.js +6 -0
  40. package/lib/Material/shaders/base/poly/vertex.js.map +1 -0
  41. package/lib/Material/shaders/base/rect/fragment.js +6 -0
  42. package/lib/Material/shaders/base/rect/fragment.js.map +1 -0
  43. package/lib/Material/shaders/base/rect/vertex.js +6 -0
  44. package/lib/Material/shaders/base/rect/vertex.js.map +1 -0
  45. package/lib/Material/shaders/line/fragment.js +6 -0
  46. package/lib/Material/shaders/line/fragment.js.map +1 -0
  47. package/lib/Material/shaders/line/vertex.js +6 -0
  48. package/lib/Material/shaders/line/vertex.js.map +1 -0
  49. package/lib/Mesh/index.d.ts +154 -0
  50. package/lib/Mesh/index.js +254 -0
  51. package/lib/Mesh/index.js.map +1 -0
  52. package/lib/Object/index.d.ts +95 -0
  53. package/lib/Object/index.js +212 -0
  54. package/lib/Object/index.js.map +1 -0
  55. package/lib/Raws/index.d.ts +87 -0
  56. package/lib/Raws/index.js +164 -0
  57. package/lib/Raws/index.js.map +1 -0
  58. package/lib/Render/BufferManager.d.ts +93 -0
  59. package/lib/Render/BufferManager.js +281 -0
  60. package/lib/Render/BufferManager.js.map +1 -0
  61. package/lib/Render/DepthManager.d.ts +22 -0
  62. package/lib/Render/DepthManager.js +95 -0
  63. package/lib/Render/DepthManager.js.map +1 -0
  64. package/lib/Render/DestroyManager.d.ts +74 -0
  65. package/lib/Render/DestroyManager.js +221 -0
  66. package/lib/Render/DestroyManager.js.map +1 -0
  67. package/lib/Render/DevelopmentValidator.d.ts +73 -0
  68. package/lib/Render/DevelopmentValidator.js +188 -0
  69. package/lib/Render/DevelopmentValidator.js.map +1 -0
  70. package/lib/Render/DrawCallManager.d.ts +58 -0
  71. package/lib/Render/DrawCallManager.js +267 -0
  72. package/lib/Render/DrawCallManager.js.map +1 -0
  73. package/lib/Render/PipelineManager.d.ts +46 -0
  74. package/lib/Render/PipelineManager.js +254 -0
  75. package/lib/Render/PipelineManager.js.map +1 -0
  76. package/lib/Render/SamplerManager.d.ts +26 -0
  77. package/lib/Render/SamplerManager.js +42 -0
  78. package/lib/Render/SamplerManager.js.map +1 -0
  79. package/lib/Render/SceneUpdate.d.ts +9 -0
  80. package/lib/Render/SceneUpdate.js +18 -0
  81. package/lib/Render/SceneUpdate.js.map +1 -0
  82. package/lib/Render/TextureManager.d.ts +33 -0
  83. package/lib/Render/TextureManager.js +152 -0
  84. package/lib/Render/TextureManager.js.map +1 -0
  85. package/lib/Render/index.d.ts +212 -0
  86. package/lib/Render/index.js +347 -0
  87. package/lib/Render/index.js.map +1 -0
  88. package/lib/Render/types.d.ts +191 -0
  89. package/lib/Scene/index.d.ts +124 -0
  90. package/lib/Scene/index.js +225 -0
  91. package/lib/Scene/index.js.map +1 -0
  92. package/lib/Style/EdgeStyle.d.ts +28 -0
  93. package/lib/Style/EdgeStyle.js +67 -0
  94. package/lib/Style/EdgeStyle.js.map +1 -0
  95. package/lib/Style/JoinStyle.d.ts +27 -0
  96. package/lib/Style/JoinStyle.js +53 -0
  97. package/lib/Style/JoinStyle.js.map +1 -0
  98. package/lib/Style/PartStyle.d.ts +50 -0
  99. package/lib/Style/PartStyle.js +92 -0
  100. package/lib/Style/PartStyle.js.map +1 -0
  101. package/lib/Style/PointsStyle.d.ts +43 -0
  102. package/lib/Style/PointsStyle.js +98 -0
  103. package/lib/Style/PointsStyle.js.map +1 -0
  104. package/lib/Style/SolidStyle.d.ts +37 -0
  105. package/lib/Style/SolidStyle.js +83 -0
  106. package/lib/Style/SolidStyle.js.map +1 -0
  107. package/lib/Style/TexturedStyle.d.ts +29 -0
  108. package/lib/Style/TexturedStyle.js +66 -0
  109. package/lib/Style/TexturedStyle.js.map +1 -0
  110. package/lib/Style/WireframeStyle.d.ts +7 -0
  111. package/lib/Style/WireframeStyle.js +13 -0
  112. package/lib/Style/WireframeStyle.js.map +1 -0
  113. package/lib/Style/WriteStyleData.d.ts +10 -0
  114. package/lib/Style/WriteStyleData.js +59 -0
  115. package/lib/Style/WriteStyleData.js.map +1 -0
  116. package/lib/Style/index.d.ts +50 -0
  117. package/lib/Style/index.js +92 -0
  118. package/lib/Style/index.js.map +1 -0
  119. package/lib/Style/types.d.ts +21 -0
  120. package/lib/Texture/Layers.d.ts +26 -0
  121. package/lib/Texture/Layers.js +42 -0
  122. package/lib/Texture/Layers.js.map +1 -0
  123. package/lib/Texture/index.d.ts +116 -0
  124. package/lib/Texture/index.js +198 -0
  125. package/lib/Texture/index.js.map +1 -0
  126. package/lib/global-types.d.ts +8 -0
  127. package/lib/index.d.ts +43 -0
  128. package/lib/index.js +34 -0
  129. package/package.json +130 -0
@@ -0,0 +1,221 @@
1
+ //#region src/scripts/Render/DestroyManager.ts
2
+ /**
3
+ * 统一负责当前 Render 的 GPU 资源销毁与无引用缓存回收。
4
+ * @class
5
+ */
6
+ var DestroyManager = class {
7
+ pipeline;
8
+ /**
9
+ * 创建销毁管理器。
10
+ * @param pipeline 当前 Render 的共享管线资源
11
+ */
12
+ constructor(pipeline) {
13
+ this.pipeline = pipeline;
14
+ }
15
+ /**
16
+ * 销毁指定 Mesh 的模型矩阵缓冲及可取得的关联资源。
17
+ * @param mesh Mesh 对象或全局 ID
18
+ * @returns 是否销毁了任意资源
19
+ */
20
+ destroyMesh(mesh) {
21
+ const meshId = typeof mesh === "number" ? mesh : mesh.id;
22
+ const matrixBuffer = this.pipeline.buffers.meshMatrix.get(meshId);
23
+ let destroyed = false;
24
+ if (matrixBuffer !== void 0) {
25
+ matrixBuffer.buffer.destroy();
26
+ this.pipeline.buffers.meshMatrix.delete(meshId);
27
+ destroyed = true;
28
+ }
29
+ const depthBuffer = this.pipeline.buffers.meshDepth.get(meshId);
30
+ if (depthBuffer !== void 0) {
31
+ depthBuffer.buffer.destroy();
32
+ this.pipeline.buffers.meshDepth.delete(meshId);
33
+ destroyed = true;
34
+ }
35
+ const styleBuffer = this.pipeline.buffers.meshStyle.get(meshId);
36
+ if (styleBuffer !== void 0) {
37
+ styleBuffer.buffer.destroy();
38
+ this.pipeline.buffers.meshStyle.delete(meshId);
39
+ destroyed = true;
40
+ }
41
+ if (typeof mesh !== "number") {
42
+ for (const texture of mesh.textures) if (texture !== void 0) destroyed = this.destroyTexture(texture) || destroyed;
43
+ if (mesh.data !== void 0) destroyed = this.destroyGeometry(mesh.data) || destroyed;
44
+ if (mesh.material !== void 0) destroyed = this.destroyMaterial(mesh.material) || destroyed;
45
+ }
46
+ return destroyed;
47
+ }
48
+ /**
49
+ * 销毁指定 Texture 的 GPUTexture 缓存。
50
+ * @param texture Texture 对象或全局 ID
51
+ * @returns 是否找到并销毁了对应资源
52
+ */
53
+ destroyTexture(texture) {
54
+ const textureId = typeof texture === "number" ? texture : texture.id;
55
+ let destroyed = false;
56
+ for (const [id, resource] of this.pipeline.textures) {
57
+ if (id !== textureId && !resource.sourceIds?.has(textureId)) continue;
58
+ this.releaseTextureBindings(resource.view);
59
+ resource.texture.destroy();
60
+ this.pipeline.textures.delete(id);
61
+ destroyed = true;
62
+ }
63
+ return destroyed;
64
+ }
65
+ /**
66
+ * 销毁所有关联此 Material 的 Mesh 实例样式缓冲,并在可取得对象时销毁其基础、边框和点型贴图。
67
+ * @param material Material 对象或全局 ID
68
+ * @returns 是否销毁了任意资源
69
+ */
70
+ destroyMaterial(material) {
71
+ const materialId = typeof material === "number" ? material : material.id;
72
+ let destroyed = false;
73
+ for (const [meshId, styleBuffer] of this.pipeline.buffers.meshStyle) {
74
+ if (styleBuffer.materialId !== materialId) continue;
75
+ styleBuffer.buffer.destroy();
76
+ this.pipeline.buffers.meshStyle.delete(meshId);
77
+ destroyed = true;
78
+ }
79
+ if (typeof material !== "number") {
80
+ const textureIds = /* @__PURE__ */ new Set();
81
+ if (material.style.solid.texture !== void 0) {
82
+ textureIds.add(material.style.solid.texture.id);
83
+ destroyed = this.destroyTexture(material.style.solid.texture) || destroyed;
84
+ }
85
+ if (material.style.edge.texture !== void 0 && !textureIds.has(material.style.edge.texture.id)) {
86
+ textureIds.add(material.style.edge.texture.id);
87
+ destroyed = this.destroyTexture(material.style.edge.texture) || destroyed;
88
+ }
89
+ if (material.style.points.texture !== void 0 && !textureIds.has(material.style.points.texture.id)) destroyed = this.destroyTexture(material.style.points.texture) || destroyed;
90
+ }
91
+ return destroyed;
92
+ }
93
+ /**
94
+ * 销毁指定 Geometry 的全部 GPUBuffer。
95
+ * @param geometry Geometry 对象或全局 ID
96
+ * @returns 是否找到并销毁了对应资源
97
+ */
98
+ destroyGeometry(geometry) {
99
+ const geometryId = typeof geometry === "number" ? geometry : geometry.id;
100
+ const geometryBuffer = this.pipeline.buffers.geometry.get(geometryId);
101
+ if (geometryBuffer === void 0) return false;
102
+ this.destroyGeometryBuffer(geometryBuffer);
103
+ this.pipeline.buffers.geometry.delete(geometryId);
104
+ return true;
105
+ }
106
+ /**
107
+ * 更新场景资源使用记录并回收所有场景均未引用的缓存。
108
+ * @param scene 需要同步资源记录的场景
109
+ * @returns 无返回值
110
+ */
111
+ trim(scene) {
112
+ scene.ensureLists();
113
+ const sceneResources = {
114
+ version: scene.version,
115
+ meshIds: /* @__PURE__ */ new Set(),
116
+ geometryIds: /* @__PURE__ */ new Set(),
117
+ materialIds: /* @__PURE__ */ new Set(),
118
+ textureIds: /* @__PURE__ */ new Set(),
119
+ pipelineKeys: /* @__PURE__ */ new Set()
120
+ };
121
+ for (const mesh of scene.drawList) sceneResources.meshIds.add(mesh.id);
122
+ for (const geometry of scene.geometryList) sceneResources.geometryIds.add(geometry.id);
123
+ for (const material of scene.materialList) sceneResources.materialIds.add(material.id);
124
+ for (const mesh of scene.drawList) if (mesh.data !== void 0 && mesh.material !== void 0) sceneResources.pipelineKeys.add(mesh.material.getPipelineKey(mesh.data.type));
125
+ for (const texture of scene.textureList) sceneResources.textureIds.add(texture.id);
126
+ this.pipeline.sceneResources.set(scene.id, sceneResources);
127
+ const usedResources = {
128
+ version: 0,
129
+ meshIds: /* @__PURE__ */ new Set(),
130
+ geometryIds: /* @__PURE__ */ new Set(),
131
+ materialIds: /* @__PURE__ */ new Set(),
132
+ textureIds: /* @__PURE__ */ new Set(),
133
+ pipelineKeys: /* @__PURE__ */ new Set()
134
+ };
135
+ for (const resources of this.pipeline.sceneResources.values()) {
136
+ for (const id of resources.meshIds) usedResources.meshIds.add(id);
137
+ for (const id of resources.geometryIds) usedResources.geometryIds.add(id);
138
+ for (const id of resources.materialIds) usedResources.materialIds.add(id);
139
+ for (const id of resources.textureIds) usedResources.textureIds.add(id);
140
+ for (const key of resources.pipelineKeys) usedResources.pipelineKeys.add(key);
141
+ }
142
+ for (const matrixBuffer of this.pipeline.buffers.meshMatrix.values()) if (!usedResources.meshIds.has(matrixBuffer.meshId)) this.destroyMesh(matrixBuffer.meshId);
143
+ for (const meshId of this.pipeline.buffers.meshDepth.keys()) if (!usedResources.meshIds.has(meshId)) this.destroyMesh(meshId);
144
+ for (const geometryId of this.pipeline.buffers.geometry.keys()) if (!usedResources.geometryIds.has(geometryId)) this.destroyGeometry(geometryId);
145
+ for (const meshId of this.pipeline.buffers.meshStyle.keys()) if (!usedResources.meshIds.has(meshId)) this.destroyMesh(meshId);
146
+ for (const [textureId, texture] of this.pipeline.textures) if (!usedResources.textureIds.has(textureId)) {
147
+ this.releaseTextureBindings(texture.view);
148
+ texture.texture.destroy();
149
+ this.pipeline.textures.delete(textureId);
150
+ }
151
+ for (const pipelineKey of this.pipeline.pipelineTemplates.keys()) if (!usedResources.pipelineKeys.has(pipelineKey)) this.pipeline.pipelineTemplates.delete(pipelineKey);
152
+ }
153
+ /**
154
+ * 销毁当前 Render 持有的全部 WebGPU 资源和设备。
155
+ * @returns 无返回值
156
+ */
157
+ destroyAll() {
158
+ const device = this.pipeline.device;
159
+ this.pipeline.context?.unconfigure();
160
+ for (const matrixBuffer of this.pipeline.buffers.meshMatrix.values()) matrixBuffer.buffer.destroy();
161
+ this.pipeline.buffers.meshMatrix.clear();
162
+ for (const depthBuffer of this.pipeline.buffers.meshDepth.values()) depthBuffer.buffer.destroy();
163
+ this.pipeline.depthTexture?.texture.destroy();
164
+ this.pipeline.depthTexture = void 0;
165
+ this.pipeline.buffers.meshDepth.clear();
166
+ for (const geometryBuffer of this.pipeline.buffers.geometry.values()) this.destroyGeometryBuffer(geometryBuffer);
167
+ for (const materialBuffer of this.pipeline.buffers.meshStyle.values()) materialBuffer.buffer.destroy();
168
+ for (const cameraBuffer of this.pipeline.buffers.cameraUniform) cameraBuffer.destroy();
169
+ for (const textureResource of this.pipeline.textures.values()) textureResource.texture.destroy();
170
+ this.pipeline.fallbackTexture?.texture.destroy();
171
+ this.pipeline.buffers.geometry.clear();
172
+ this.pipeline.buffers.meshStyle.clear();
173
+ this.pipeline.buffers.cameraUniform.length = 0;
174
+ this.pipeline.textures.clear();
175
+ this.pipeline.samplers.clear();
176
+ this.pipeline.pipelineTemplates.clear();
177
+ this.pipeline.sceneResources.clear();
178
+ this.pipeline.adapter = void 0;
179
+ this.pipeline.device = void 0;
180
+ this.pipeline.context = void 0;
181
+ this.pipeline.format = void 0;
182
+ this.pipeline.defaultBindGroupLayout = void 0;
183
+ this.pipeline.baseMaterialBindGroupLayout = void 0;
184
+ this.pipeline.fallbackTexture = void 0;
185
+ device?.destroy();
186
+ }
187
+ /**
188
+ * 销毁 Geometry 所有绘制模式已经创建的全部缓冲。
189
+ * @param geometryBuffer Geometry GPU 缓存集合
190
+ * @returns 无返回值
191
+ */
192
+ destroyGeometryBuffer(geometryBuffer) {
193
+ for (const entry of this.pipeline.buffers.meshMatrix.values()) if (entry.bindGroup?.geometry === geometryBuffer.uniform) entry.bindGroup = void 0;
194
+ const DestroyPart = (part) => {
195
+ part?.vertex.destroy();
196
+ part?.normal.destroy();
197
+ part?.uv.destroy();
198
+ part?.index.destroy();
199
+ part?.miterScale?.destroy();
200
+ part?.position?.destroy();
201
+ part?.vertexType?.destroy();
202
+ };
203
+ DestroyPart(geometryBuffer.geometry);
204
+ DestroyPart(geometryBuffer.linePoints);
205
+ geometryBuffer.uniform.destroy();
206
+ }
207
+ /**
208
+ * 解除引用某个纹理视图的缓存绑定,不影响其他 Mesh。
209
+ * @param view 即将销毁的 GPU 纹理视图
210
+ */
211
+ releaseTextureBindings(view) {
212
+ for (const entry of this.pipeline.buffers.meshStyle.values()) {
213
+ const binding = entry.bindGroup;
214
+ if (binding?.baseView === view || binding?.edgeView === view || binding?.pointsView === view) entry.bindGroup = void 0;
215
+ }
216
+ }
217
+ };
218
+ //#endregion
219
+ export { DestroyManager as default };
220
+
221
+ //# sourceMappingURL=DestroyManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DestroyManager.js","names":[],"sources":["../../src/scripts/Render/DestroyManager.ts"],"sourcesContent":["import type { GeoData } from \"../Geometry\";\nimport type { Material2d } from \"../global-types\";\nimport type { MeshLike } from \"../Mesh\";\nimport type Scene from \"../Scene\";\nimport type { TextureResource } from \"../Texture/Layers\";\nimport type {\n GeometryBufferLike,\n GeometryBufferPartLike,\n PipelineLike,\n SceneResourcesLike,\n} from \"./types\";\n\ninterface DestroyManagerLike {\n destroyMesh(mesh: MeshLike | number): boolean;\n destroyTexture(texture: TextureResource | number): boolean;\n destroyMaterial(material: Material2d | number): boolean;\n destroyGeometry(geometry: GeoData | number): boolean;\n trim(scene: Scene): void;\n destroyAll(): void;\n}\n\n/**\n * 统一负责当前 Render 的 GPU 资源销毁与无引用缓存回收。\n * @class\n */\nclass DestroyManager implements DestroyManagerLike {\n private readonly pipeline: PipelineLike;\n\n /**\n * 创建销毁管理器。\n * @param pipeline 当前 Render 的共享管线资源\n */\n public constructor(pipeline: PipelineLike) {\n this.pipeline = pipeline;\n }\n\n /**\n * 销毁指定 Mesh 的模型矩阵缓冲及可取得的关联资源。\n * @param mesh Mesh 对象或全局 ID\n * @returns 是否销毁了任意资源\n */\n public destroyMesh(mesh: MeshLike | number): boolean {\n const meshId: number = typeof mesh === \"number\" ? mesh : mesh.id;\n const matrixBuffer = this.pipeline.buffers.meshMatrix.get(meshId);\n let destroyed: boolean = false;\n\n if (matrixBuffer !== undefined) {\n matrixBuffer.buffer.destroy();\n this.pipeline.buffers.meshMatrix.delete(meshId);\n destroyed = true;\n }\n\n const depthBuffer = this.pipeline.buffers.meshDepth.get(meshId);\n if (depthBuffer !== undefined) {\n depthBuffer.buffer.destroy();\n this.pipeline.buffers.meshDepth.delete(meshId);\n destroyed = true;\n }\n const styleBuffer = this.pipeline.buffers.meshStyle.get(meshId);\n if (styleBuffer !== undefined) {\n styleBuffer.buffer.destroy();\n this.pipeline.buffers.meshStyle.delete(meshId);\n destroyed = true;\n }\n\n if (typeof mesh !== \"number\") {\n for (const texture of mesh.textures) {\n if (texture !== undefined) destroyed = this.destroyTexture(texture) || destroyed;\n }\n if (mesh.data !== undefined) {\n destroyed = this.destroyGeometry(mesh.data) || destroyed;\n }\n if (mesh.material !== undefined) {\n destroyed = this.destroyMaterial(mesh.material) || destroyed;\n }\n }\n\n return destroyed;\n }\n\n /**\n * 销毁指定 Texture 的 GPUTexture 缓存。\n * @param texture Texture 对象或全局 ID\n * @returns 是否找到并销毁了对应资源\n */\n public destroyTexture(texture: TextureResource | number): boolean {\n const textureId: number = typeof texture === \"number\" ? texture : texture.id;\n let destroyed: boolean = false;\n for (const [id, resource] of this.pipeline.textures) {\n if (id !== textureId && !resource.sourceIds?.has(textureId)) continue;\n this.releaseTextureBindings(resource.view);\n resource.texture.destroy();\n this.pipeline.textures.delete(id);\n destroyed = true;\n }\n return destroyed;\n }\n\n /**\n * 销毁所有关联此 Material 的 Mesh 实例样式缓冲,并在可取得对象时销毁其基础、边框和点型贴图。\n * @param material Material 对象或全局 ID\n * @returns 是否销毁了任意资源\n */\n public destroyMaterial(material: Material2d | number): boolean {\n const materialId: number = typeof material === \"number\" ? material : material.id;\n let destroyed: boolean = false;\n for (const [meshId, styleBuffer] of this.pipeline.buffers.meshStyle) {\n if (styleBuffer.materialId !== materialId) continue;\n styleBuffer.buffer.destroy();\n this.pipeline.buffers.meshStyle.delete(meshId);\n destroyed = true;\n }\n\n if (typeof material !== \"number\") {\n const textureIds: Set<number> = new Set<number>();\n\n if (material.style.solid.texture !== undefined) {\n textureIds.add(material.style.solid.texture.id);\n destroyed = this.destroyTexture(material.style.solid.texture) || destroyed;\n }\n if (material.style.edge.texture !== undefined && !textureIds.has(material.style.edge.texture.id)) {\n textureIds.add(material.style.edge.texture.id);\n destroyed = this.destroyTexture(material.style.edge.texture) || destroyed;\n }\n if (material.style.points.texture !== undefined && !textureIds.has(material.style.points.texture.id)) {\n destroyed = this.destroyTexture(material.style.points.texture) || destroyed;\n }\n }\n\n return destroyed;\n }\n\n /**\n * 销毁指定 Geometry 的全部 GPUBuffer。\n * @param geometry Geometry 对象或全局 ID\n * @returns 是否找到并销毁了对应资源\n */\n public destroyGeometry(geometry: GeoData | number): boolean {\n const geometryId: number = typeof geometry === \"number\" ? geometry : geometry.id;\n const geometryBuffer: GeometryBufferLike | undefined = this.pipeline.buffers.geometry.get(geometryId);\n\n if (geometryBuffer === undefined) {\n return false;\n }\n\n this.destroyGeometryBuffer(geometryBuffer);\n this.pipeline.buffers.geometry.delete(geometryId);\n return true;\n }\n\n /**\n * 更新场景资源使用记录并回收所有场景均未引用的缓存。\n * @param scene 需要同步资源记录的场景\n * @returns 无返回值\n */\n public trim(scene: Scene): void {\n scene.ensureLists();\n const sceneResources: SceneResourcesLike = {\n version: scene.version,\n meshIds: new Set<number>(),\n geometryIds: new Set<number>(),\n materialIds: new Set<number>(),\n textureIds: new Set<number>(),\n pipelineKeys: new Set<string>(),\n };\n\n for (const mesh of scene.drawList) sceneResources.meshIds.add(mesh.id);\n for (const geometry of scene.geometryList) sceneResources.geometryIds.add(geometry.id);\n for (const material of scene.materialList) {\n sceneResources.materialIds.add(material.id);\n }\n for (const mesh of scene.drawList) {\n if (mesh.data !== undefined && mesh.material !== undefined) {\n sceneResources.pipelineKeys.add(mesh.material.getPipelineKey(mesh.data.type));\n }\n }\n for (const texture of scene.textureList) sceneResources.textureIds.add(texture.id);\n\n this.pipeline.sceneResources.set(scene.id, sceneResources);\n\n const usedResources: SceneResourcesLike = {\n version: 0,\n meshIds: new Set<number>(),\n geometryIds: new Set<number>(),\n materialIds: new Set<number>(),\n textureIds: new Set<number>(),\n pipelineKeys: new Set<string>(),\n };\n\n for (const resources of this.pipeline.sceneResources.values()) {\n for (const id of resources.meshIds) usedResources.meshIds.add(id);\n for (const id of resources.geometryIds) usedResources.geometryIds.add(id);\n for (const id of resources.materialIds) usedResources.materialIds.add(id);\n for (const id of resources.textureIds) usedResources.textureIds.add(id);\n for (const key of resources.pipelineKeys) usedResources.pipelineKeys.add(key);\n }\n\n for (const matrixBuffer of this.pipeline.buffers.meshMatrix.values()) {\n if (!usedResources.meshIds.has(matrixBuffer.meshId)) this.destroyMesh(matrixBuffer.meshId);\n }\n for (const meshId of this.pipeline.buffers.meshDepth.keys()) {\n if (!usedResources.meshIds.has(meshId)) this.destroyMesh(meshId);\n }\n for (const geometryId of this.pipeline.buffers.geometry.keys()) {\n if (!usedResources.geometryIds.has(geometryId)) this.destroyGeometry(geometryId);\n }\n for (const meshId of this.pipeline.buffers.meshStyle.keys()) {\n if (!usedResources.meshIds.has(meshId)) this.destroyMesh(meshId);\n }\n for (const [textureId, texture] of this.pipeline.textures) {\n // trim 只清理这个资源;源图单层缓存闲置不代表其他层组也闲置。\n if (!usedResources.textureIds.has(textureId)) {\n this.releaseTextureBindings(texture.view);\n texture.texture.destroy();\n this.pipeline.textures.delete(textureId);\n }\n }\n for (const pipelineKey of this.pipeline.pipelineTemplates.keys()) {\n if (!usedResources.pipelineKeys.has(pipelineKey)) {\n // GPURenderPipeline 没有 destroy(),删除引用后交由浏览器回收。\n this.pipeline.pipelineTemplates.delete(pipelineKey);\n }\n }\n }\n\n /**\n * 销毁当前 Render 持有的全部 WebGPU 资源和设备。\n * @returns 无返回值\n */\n public destroyAll(): void {\n const device: GPUDevice | undefined = this.pipeline.device;\n\n this.pipeline.context?.unconfigure();\n\n for (const matrixBuffer of this.pipeline.buffers.meshMatrix.values()) matrixBuffer.buffer.destroy();\n // 全量销毁先清绑定,后续逐个释放几何时无需再扫描这些 Mesh。\n this.pipeline.buffers.meshMatrix.clear();\n for (const depthBuffer of this.pipeline.buffers.meshDepth.values()) depthBuffer.buffer.destroy();\n this.pipeline.depthTexture?.texture.destroy();\n this.pipeline.depthTexture = undefined;\n this.pipeline.buffers.meshDepth.clear();\n for (const geometryBuffer of this.pipeline.buffers.geometry.values()) {\n this.destroyGeometryBuffer(geometryBuffer);\n }\n for (const materialBuffer of this.pipeline.buffers.meshStyle.values()) materialBuffer.buffer.destroy();\n for (const cameraBuffer of this.pipeline.buffers.cameraUniform) cameraBuffer.destroy();\n for (const textureResource of this.pipeline.textures.values()) textureResource.texture.destroy();\n\n this.pipeline.fallbackTexture?.texture.destroy();\n this.pipeline.buffers.geometry.clear();\n this.pipeline.buffers.meshStyle.clear();\n this.pipeline.buffers.cameraUniform.length = 0;\n this.pipeline.textures.clear();\n this.pipeline.samplers.clear();\n this.pipeline.pipelineTemplates.clear();\n this.pipeline.sceneResources.clear();\n this.pipeline.adapter = undefined;\n this.pipeline.device = undefined;\n this.pipeline.context = undefined;\n this.pipeline.format = undefined;\n this.pipeline.defaultBindGroupLayout = undefined;\n this.pipeline.baseMaterialBindGroupLayout = undefined;\n this.pipeline.fallbackTexture = undefined;\n\n device?.destroy();\n }\n\n /**\n * 销毁 Geometry 所有绘制模式已经创建的全部缓冲。\n * @param geometryBuffer Geometry GPU 缓存集合\n * @returns 无返回值\n */\n private destroyGeometryBuffer(geometryBuffer: GeometryBufferLike): void {\n // BindGroup 没有 destroy(),解除引用即可;避免保留已销毁几何的绑定。\n for (const entry of this.pipeline.buffers.meshMatrix.values()) {\n if (entry.bindGroup?.geometry === geometryBuffer.uniform) entry.bindGroup = undefined;\n }\n const DestroyPart = (part: GeometryBufferPartLike | undefined): void => {\n part?.vertex.destroy();\n part?.normal.destroy();\n part?.uv.destroy();\n part?.index.destroy();\n part?.miterScale?.destroy();\n part?.position?.destroy();\n part?.vertexType?.destroy();\n };\n\n DestroyPart(geometryBuffer.geometry);\n DestroyPart(geometryBuffer.linePoints);\n geometryBuffer.uniform.destroy();\n }\n\n /**\n * 解除引用某个纹理视图的缓存绑定,不影响其他 Mesh。\n * @param view 即将销毁的 GPU 纹理视图\n */\n private releaseTextureBindings(view: GPUTextureView): void {\n for (const entry of this.pipeline.buffers.meshStyle.values()) {\n const binding = entry.bindGroup;\n if (binding?.baseView === view || binding?.edgeView === view || binding?.pointsView === view) {\n entry.bindGroup = undefined;\n }\n }\n }\n}\n\nexport default DestroyManager;\nexport type { DestroyManagerLike };\n"],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import type Camera from "../Camera";
2
+ import type { GeoData } from "../Geometry";
3
+ import type { Material2d } from "../global-types";
4
+ import type { MeshLike } from "../Mesh";
5
+ interface DevelopmentValidatorLike {
6
+ validateCamera(camera: Camera): void;
7
+ validateMaterial(material: Material2d): void;
8
+ validateGeometry(geometry: GeoData): void;
9
+ validateMesh(mesh: MeshLike): void;
10
+ }
11
+ /**
12
+ * 开发模式 GPUBuffer 数据与渲染配置校验器。
13
+ * 只报告 TypeScript 无法约束的运行时数据问题,并按对象和问题类型去重。
14
+ * @class
15
+ */
16
+ declare class DevelopmentValidator implements DevelopmentValidatorLike {
17
+ private readonly reported;
18
+ /**
19
+ * 检查 Camera 是否能生成有效矩阵与 Uniform。
20
+ * @param camera 当前 Camera
21
+ * @returns 无返回值
22
+ */
23
+ validateCamera(camera: Camera): void;
24
+ /**
25
+ * 检查材质样式数据和分区配置。
26
+ * @param material 当前材质
27
+ * @returns 无返回值
28
+ */
29
+ validateMaterial(material: Material2d): void;
30
+ /**
31
+ * 检查 Geometry 各缓冲分区的长度、有限值和索引范围。
32
+ * @param geometry 当前 Geometry
33
+ * @returns 无返回值
34
+ */
35
+ validateGeometry(geometry: GeoData): void;
36
+ /**
37
+ * 检查 Mesh 的 Geometry 与 Material 是否能组成目标边框绘制模式。
38
+ * @param mesh 当前 Mesh
39
+ * @returns 无返回值
40
+ */
41
+ validateMesh(mesh: MeshLike): void;
42
+ /**
43
+ * 检查单个几何缓冲分区。
44
+ * @param geometryId Geometry ID
45
+ * @param name 缓冲分区名
46
+ * @param data CPU 几何数据
47
+ * @param primitiveSize 每个图元使用的索引数
48
+ * @param requireMiterScale 是否要求 miterScale
49
+ * @param source 日志附带对象
50
+ * @returns 无返回值
51
+ */
52
+ private validatePart;
53
+ /**
54
+ * 输出一次开发模式警告。
55
+ * @param code 问题代码
56
+ * @param objectId 对象 ID
57
+ * @param message 问题说明
58
+ * @param source 关联对象
59
+ * @returns 无返回值
60
+ */
61
+ private warn;
62
+ /**
63
+ * 输出一次开发模式错误。
64
+ * @param code 问题代码
65
+ * @param objectId 对象 ID
66
+ * @param message 问题说明
67
+ * @param source 关联对象
68
+ * @returns 无返回值
69
+ */
70
+ private error;
71
+ }
72
+ export default DevelopmentValidator;
73
+ export type { DevelopmentValidatorLike };
@@ -0,0 +1,188 @@
1
+ import IMesh from "../IMesh/index.js";
2
+ //#region src/scripts/Render/DevelopmentValidator.ts
3
+ /**
4
+ * 开发模式 GPUBuffer 数据与渲染配置校验器。
5
+ * 只报告 TypeScript 无法约束的运行时数据问题,并按对象和问题类型去重。
6
+ * @class
7
+ */
8
+ var DevelopmentValidator = class {
9
+ reported = /* @__PURE__ */ new Set();
10
+ /**
11
+ * 检查 Camera 是否能生成有效矩阵与 Uniform。
12
+ * @param camera 当前 Camera
13
+ * @returns 无返回值
14
+ */
15
+ validateCamera(camera) {
16
+ if (!Number.isFinite(camera.width) || camera.width <= 0) this.error("CAMERA_WIDTH", camera.id, "Camera width 必须是大于 0 的有限数。", camera);
17
+ if (!Number.isFinite(camera.height) || camera.height <= 0) this.error("CAMERA_HEIGHT", camera.id, "Camera height 必须是大于 0 的有限数。", camera);
18
+ if (!Number.isFinite(camera.zoom) || camera.zoom <= 0) this.error("CAMERA_ZOOM", camera.id, "Camera zoom 必须是大于 0 的有限数。", camera);
19
+ }
20
+ /**
21
+ * 检查材质样式数据和分区配置。
22
+ * @param material 当前材质
23
+ * @returns 无返回值
24
+ */
25
+ validateMaterial(material) {
26
+ if (material.transparent && material.depthWrite) this.warn("TRANSPARENT_DEPTH_WRITE", material.id, "透明混合同时写入深度可能挡住后方透明片元;通常使用 depthTest=true、depthWrite=false。", material);
27
+ if (!material.depthTest && material.depthWrite) this.warn("DEPTH_WRITE_WITHOUT_TEST", material.id, "已关闭深度测试但仍写深度;它不会检查场景遮挡,通常 UI 两项都关闭。", material);
28
+ const { solid, wireframe, edge, points } = material.style;
29
+ for (const part of [
30
+ solid,
31
+ wireframe,
32
+ edge,
33
+ points
34
+ ]) if (!Number.isFinite(part.opacity) || part.opacity < 0 || part.opacity > 1) this.error("STYLE_OPACITY_" + part.area, material.id, "分区透明度必须位于 0 到 1。", material);
35
+ for (const part of [
36
+ solid,
37
+ edge,
38
+ points
39
+ ]) if (part.texture !== void 0 && !part.enabled) this.warn("UNUSED_TEXTURE_" + part.area, material.id, "分区已设置贴图,但 enabled 尚未开启。", material);
40
+ if (points.enabled && !points.vertices && !points.midpoints) this.warn("EMPTY_POINT_SELECTION", material.id, "关键点已开启,但未选择顶点或边中点;不会生成点型。", material);
41
+ }
42
+ /**
43
+ * 检查 Geometry 各缓冲分区的长度、有限值和索引范围。
44
+ * @param geometry 当前 Geometry
45
+ * @returns 无返回值
46
+ */
47
+ validateGeometry(geometry) {
48
+ if (geometry.geometry !== void 0) {
49
+ if (geometry.normal === void 0 || geometry.uv === void 0 || geometry.index === void 0 || geometry.vertexType === void 0 || geometry.position === void 0 || geometry.miterScale === void 0) this.error("GEOMETRY_MISSING", geometry.id, "合并三角面缺少 normal、uv、index、vertexType、position 或 miterScale。", geometry);
50
+ else {
51
+ const vertexCount = geometry.geometry.length / 2;
52
+ this.validatePart(geometry.id, "geometry", {
53
+ geometry: geometry.geometry,
54
+ normal: geometry.normal,
55
+ uv: geometry.uv,
56
+ index: geometry.index,
57
+ miterScale: geometry.miterScale
58
+ }, 3, true, geometry);
59
+ if (geometry.vertexType.length !== vertexCount || geometry.vertexType.some((value) => value !== 0 && value !== .5 && value !== 1)) this.error("VERTEX_TYPE", geometry.id, "vertexType 必须逐顶点对应,且只能为 0、0.5、1。", geometry);
60
+ if (geometry.position.length !== geometry.geometry.length || GetInvalidNumberIndex(geometry.position) !== -1) this.error("GEOMETRY_POSITION", geometry.id, "position 必须与 geometry 等长且全部为有限数。", geometry);
61
+ for (let offset = 0; offset < geometry.index.length; offset += 3) {
62
+ const type = geometry.vertexType[geometry.index[offset]];
63
+ if (geometry.vertexType[geometry.index[offset + 1]] !== type || geometry.vertexType[geometry.index[offset + 2]] !== type) {
64
+ this.error("TRIANGLE_VERTEX_TYPE", geometry.id, "一个三角面的三个顶点必须属于同一 vertexType。", geometry);
65
+ break;
66
+ }
67
+ }
68
+ }
69
+ }
70
+ if (geometry.linePoints !== void 0) this.validatePart(geometry.id, "linePoints", geometry.linePoints, 2, false, geometry);
71
+ if (geometry.style.wireframe.enabled && geometry.linePoints === void 0) this.error("WIREFRAME_DATA", geometry.id, "Geometry wireframe 已开启,但没有生成 linePoints。", geometry);
72
+ if (geometry.style.edge.uvRepeat !== 1 && !geometry.style.wireframe.enabled && !geometry.style.edge.enabled) this.warn("UNUSED_UV_REPEAT", geometry.id, "uvRepeat 只作用于边框,当前值不会被使用。", geometry);
73
+ if (geometry.uniformData.length !== 4 || GetInvalidNumberIndex(geometry.uniformData) !== -1) this.error("GEOMETRY_UNIFORM", geometry.id, "Geometry uniformData 必须包含 4 个有限数。", geometry);
74
+ }
75
+ /**
76
+ * 检查 Mesh 的 Geometry 与 Material 是否能组成目标边框绘制模式。
77
+ * @param mesh 当前 Mesh
78
+ * @returns 无返回值
79
+ */
80
+ validateMesh(mesh) {
81
+ const geometry = mesh.data;
82
+ const material = mesh.material;
83
+ if (geometry === void 0 || material === void 0) {
84
+ this.error("MESH_RESOURCE", mesh.id, "Mesh 缺少 Geometry 或 Material。", mesh);
85
+ return;
86
+ }
87
+ if (geometry.style.solid.enabled && geometry.type !== "Rect2d" && geometry.type !== "Poly2D") this.error("UNSUPPORTED_SOLID_TYPE", mesh.id, "未提供该类型的实体面 Shader。", mesh);
88
+ if (mesh instanceof IMesh) {
89
+ if (geometry.style.wireframe.enabled && mesh.count > 1) this.warn("IMESH_NATIVE_LINES", mesh.id, "原生 line-list 使用逐实例顺序回退;只用三角面时才合并为一次绘制。", mesh);
90
+ for (const raw of mesh.raws.map.values()) {
91
+ if (geometry.type === "Poly2D" && raw.style.solid.borderWidth > 0) this.warn("POLY_SDF_BORDER", mesh.id, "Poly2D 不使用矩形 SDF 边框,请启用 edge 分区。", mesh);
92
+ if (geometry.type === "Poly2D" && (raw.style.join.type !== geometry.style.join.type || raw.style.join.seg !== geometry.style.join.seg)) this.warn("IMESH_JOIN_TEMPLATE", mesh.id, "连接类型和精度由 Geometry.style.join 决定,Raw 不生成独立拓扑。", mesh);
93
+ for (const area of [
94
+ "solid",
95
+ "wireframe",
96
+ "edge",
97
+ "points"
98
+ ]) if (raw.style[area].enabled && !geometry.style[area].enabled) this.warn("IMESH_MISSING_" + area, mesh.id, "实例启用了模板未生成的 " + area + " 分区;请先配置 Geometry.style。", mesh);
99
+ for (const area of [
100
+ "solid",
101
+ "edge",
102
+ "points"
103
+ ]) if (raw.style[area].addressModeU !== material.style[area].addressModeU || raw.style[area].addressModeV !== material.style[area].addressModeV) this.warn("IMESH_SAMPLER_" + area, mesh.id, "一次绘制共用 Material.style 的采样器,Raw.style 的寻址方式不单独生效。", mesh);
104
+ if (raw.style.edge.enabled && (raw.style.edge.width !== geometry.style.edge.width || raw.style.edge.uvRepeat !== geometry.style.edge.uvRepeat)) this.warn("IMESH_EDGE_TEMPLATE", mesh.id, "实例实体边框宽度和 UV Repeat 由共用 Geometry.style 生成,Raw 不重建独立几何。", mesh);
105
+ if (raw.style.points.enabled && (raw.style.points.radius !== geometry.style.points.radius || raw.style.points.segments !== geometry.style.points.segments || raw.style.points.vertices !== geometry.style.points.vertices || raw.style.points.midpoints !== geometry.style.points.midpoints || raw.style.points.minPointsLength !== geometry.style.points.minPointsLength || raw.style.points.minEdgePointsLength !== geometry.style.points.minEdgePointsLength)) this.warn("IMESH_POINTS_TEMPLATE", mesh.id, "关键点半径、段数、选择规则由共用 Geometry.style 决定。", mesh);
106
+ }
107
+ return;
108
+ }
109
+ if (geometry.type === "Poly2D" && material.style.solid.borderWidth > 0) this.warn("POLY_SDF_BORDER", mesh.id, "Poly2D 不使用矩形 SDF 边框,请启用 edge 分区。", mesh);
110
+ if (geometry.type === "Poly2D" && (geometry.style.join.type !== material.style.join.type || geometry.style.join.seg !== material.style.join.seg)) this.warn("POLY_JOIN_STYLE", mesh.id, "连接拓扑由 Geometry.style.join 生成,建议几何与材质共享 Style。", mesh);
111
+ for (const area of [
112
+ "solid",
113
+ "wireframe",
114
+ "edge",
115
+ "points"
116
+ ]) if (geometry.style[area].enabled !== material.style[area].enabled) this.warn("STYLE_ENABLED_" + area, mesh.id, "几何与材质的分区开关不同;建议引用同一个 Style。", mesh);
117
+ if (geometry.style !== material.style && geometry.style.edge.enabled && (geometry.style.edge.width !== material.style.edge.width || geometry.style.edge.borderAlign !== material.style.edge.borderAlign)) this.warn("STYLE_EDGE_PARAMETERS", mesh.id, "几何与材质的实体边框参数不同;几何宽度参与生成,建议共享 Style。", mesh);
118
+ }
119
+ /**
120
+ * 检查单个几何缓冲分区。
121
+ * @param geometryId Geometry ID
122
+ * @param name 缓冲分区名
123
+ * @param data CPU 几何数据
124
+ * @param primitiveSize 每个图元使用的索引数
125
+ * @param requireMiterScale 是否要求 miterScale
126
+ * @param source 日志附带对象
127
+ * @returns 无返回值
128
+ */
129
+ validatePart(geometryId, name, data, primitiveSize, requireMiterScale, source) {
130
+ const vertexCount = data.geometry.length / 2;
131
+ if (data.geometry.length % 2 !== 0) this.error("GEOMETRY_VERTEX_" + name, geometryId, name + " 顶点数组长度必须是 2 的倍数。", source);
132
+ if (data.normal.length !== data.geometry.length) this.error("GEOMETRY_NORMAL_" + name, geometryId, name + " 法线数量必须与顶点数量一致。", source);
133
+ if (data.uv.length !== data.geometry.length) this.error("GEOMETRY_UV_" + name, geometryId, name + " UV 数量必须与顶点数量一致。", source);
134
+ if (data.index.length % primitiveSize !== 0) this.error("GEOMETRY_INDEX_COUNT_" + name, geometryId, name + " 索引数量与图元类型不匹配。", source);
135
+ if (data.geometry.length === 0 || data.index.length === 0) this.warn("EMPTY_GEOMETRY_" + name, geometryId, name + " 不包含可绘制图元。", source);
136
+ if (GetInvalidNumberIndex(data.geometry) !== -1 || GetInvalidNumberIndex(data.normal) !== -1 || GetInvalidNumberIndex(data.uv) !== -1) this.error("GEOMETRY_NUMBER_" + name, geometryId, name + " 包含 NaN 或 Infinity。", source);
137
+ for (const index of data.index) if (index >= vertexCount) {
138
+ this.error("GEOMETRY_INDEX_RANGE_" + name, geometryId, name + " 存在越界索引 " + String(index) + "。", source);
139
+ break;
140
+ }
141
+ if (requireMiterScale && data.miterScale === void 0) this.error("GEOMETRY_MITER_MISSING_" + name, geometryId, name + " 缺少 miterScale。", source);
142
+ else if (data.miterScale !== void 0) {
143
+ if (data.miterScale.length !== vertexCount) this.error("GEOMETRY_MITER_COUNT_" + name, geometryId, name + " miterScale 数量必须与顶点数量一致。", source);
144
+ if (GetInvalidNumberIndex(data.miterScale) !== -1) this.error("GEOMETRY_MITER_NUMBER_" + name, geometryId, name + " miterScale 包含 NaN 或 Infinity。", source);
145
+ }
146
+ }
147
+ /**
148
+ * 输出一次开发模式警告。
149
+ * @param code 问题代码
150
+ * @param objectId 对象 ID
151
+ * @param message 问题说明
152
+ * @param source 关联对象
153
+ * @returns 无返回值
154
+ */
155
+ warn(code, objectId, message, source) {
156
+ const key = "warn_" + code + "_" + String(objectId);
157
+ if (this.reported.has(key)) return;
158
+ this.reported.add(key);
159
+ console.warn("[BPLineJS:" + code + "] " + message, source);
160
+ }
161
+ /**
162
+ * 输出一次开发模式错误。
163
+ * @param code 问题代码
164
+ * @param objectId 对象 ID
165
+ * @param message 问题说明
166
+ * @param source 关联对象
167
+ * @returns 无返回值
168
+ */
169
+ error(code, objectId, message, source) {
170
+ const key = "error_" + code + "_" + String(objectId);
171
+ if (this.reported.has(key)) return;
172
+ this.reported.add(key);
173
+ console.error("[BPLineJS:" + code + "] " + message, source);
174
+ }
175
+ };
176
+ /**
177
+ * 获取数组中首个非有限数索引。
178
+ * @param data 待检查数值数组
179
+ * @returns 非有限数索引,全部有效时返回 -1
180
+ */
181
+ var GetInvalidNumberIndex = (data) => {
182
+ for (let index = 0; index < data.length; index += 1) if (!Number.isFinite(data[index])) return index;
183
+ return -1;
184
+ };
185
+ //#endregion
186
+ export { DevelopmentValidator as default };
187
+
188
+ //# sourceMappingURL=DevelopmentValidator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DevelopmentValidator.js","names":[],"sources":["../../src/scripts/Render/DevelopmentValidator.ts"],"sourcesContent":["import IMesh from \"../IMesh\";\nimport type Camera from \"../Camera\";\nimport type { GeoData, GeoPartDataLike } from \"../Geometry\";\nimport type { Material2d } from \"../global-types\";\nimport type { MeshLike } from \"../Mesh\";\n\ninterface DevelopmentValidatorLike {\n validateCamera(camera: Camera): void;\n validateMaterial(material: Material2d): void;\n validateGeometry(geometry: GeoData): void;\n validateMesh(mesh: MeshLike): void;\n}\n\n/**\n * 开发模式 GPUBuffer 数据与渲染配置校验器。\n * 只报告 TypeScript 无法约束的运行时数据问题,并按对象和问题类型去重。\n * @class\n */\nclass DevelopmentValidator implements DevelopmentValidatorLike {\n private readonly reported: Set<string> = new Set<string>();\n\n /**\n * 检查 Camera 是否能生成有效矩阵与 Uniform。\n * @param camera 当前 Camera\n * @returns 无返回值\n */\n public validateCamera(camera: Camera): void {\n if (!Number.isFinite(camera.width) || camera.width <= 0) {\n this.error(\"CAMERA_WIDTH\", camera.id, \"Camera width 必须是大于 0 的有限数。\", camera);\n }\n if (!Number.isFinite(camera.height) || camera.height <= 0) {\n this.error(\"CAMERA_HEIGHT\", camera.id, \"Camera height 必须是大于 0 的有限数。\", camera);\n }\n if (!Number.isFinite(camera.zoom) || camera.zoom <= 0) {\n this.error(\"CAMERA_ZOOM\", camera.id, \"Camera zoom 必须是大于 0 的有限数。\", camera);\n }\n }\n\n /**\n * 检查材质样式数据和分区配置。\n * @param material 当前材质\n * @returns 无返回值\n */\n public validateMaterial(material: Material2d): void {\n if (material.transparent && material.depthWrite) {\n this.warn(\"TRANSPARENT_DEPTH_WRITE\", material.id, \"透明混合同时写入深度可能挡住后方透明片元;通常使用 depthTest=true、depthWrite=false。\", material);\n }\n if (!material.depthTest && material.depthWrite) {\n this.warn(\"DEPTH_WRITE_WITHOUT_TEST\", material.id, \"已关闭深度测试但仍写深度;它不会检查场景遮挡,通常 UI 两项都关闭。\", material);\n }\n const { solid, wireframe, edge, points } = material.style;\n for (const part of [solid, wireframe, edge, points]) {\n if (!Number.isFinite(part.opacity) || part.opacity < 0 || part.opacity > 1) {\n this.error(\"STYLE_OPACITY_\" + part.area, material.id, \"分区透明度必须位于 0 到 1。\", material);\n }\n }\n for (const part of [solid, edge, points]) {\n if (part.texture !== undefined && !part.enabled) {\n this.warn(\"UNUSED_TEXTURE_\" + part.area, material.id, \"分区已设置贴图,但 enabled 尚未开启。\", material);\n }\n }\n if (points.enabled && !points.vertices && !points.midpoints) {\n this.warn(\"EMPTY_POINT_SELECTION\", material.id, \"关键点已开启,但未选择顶点或边中点;不会生成点型。\", material);\n }\n }\n\n /**\n * 检查 Geometry 各缓冲分区的长度、有限值和索引范围。\n * @param geometry 当前 Geometry\n * @returns 无返回值\n */\n public validateGeometry(geometry: GeoData): void {\n if (geometry.geometry !== undefined) {\n if (geometry.normal === undefined || geometry.uv === undefined || geometry.index === undefined\n || geometry.vertexType === undefined || geometry.position === undefined || geometry.miterScale === undefined) {\n this.error(\"GEOMETRY_MISSING\", geometry.id, \"合并三角面缺少 normal、uv、index、vertexType、position 或 miterScale。\", geometry);\n } else {\n const vertexCount: number = geometry.geometry.length / 2;\n this.validatePart(geometry.id, \"geometry\", {\n geometry: geometry.geometry, normal: geometry.normal, uv: geometry.uv,\n index: geometry.index, miterScale: geometry.miterScale,\n }, 3, true, geometry);\n if (geometry.vertexType.length !== vertexCount\n || geometry.vertexType.some((value: number): boolean => value !== 0 && value !== 0.5 && value !== 1)) {\n this.error(\"VERTEX_TYPE\", geometry.id, \"vertexType 必须逐顶点对应,且只能为 0、0.5、1。\", geometry);\n }\n if (geometry.position.length !== geometry.geometry.length || GetInvalidNumberIndex(geometry.position) !== -1) {\n this.error(\"GEOMETRY_POSITION\", geometry.id, \"position 必须与 geometry 等长且全部为有限数。\", geometry);\n }\n for (let offset: number = 0; offset < geometry.index.length; offset += 3) {\n const type: number = geometry.vertexType[geometry.index[offset]];\n if (geometry.vertexType[geometry.index[offset + 1]] !== type || geometry.vertexType[geometry.index[offset + 2]] !== type) {\n this.error(\"TRIANGLE_VERTEX_TYPE\", geometry.id, \"一个三角面的三个顶点必须属于同一 vertexType。\", geometry);\n break;\n }\n }\n }\n }\n if (geometry.linePoints !== undefined) {\n this.validatePart(geometry.id, \"linePoints\", geometry.linePoints, 2, false, geometry);\n }\n if (geometry.style.wireframe.enabled && geometry.linePoints === undefined) {\n this.error(\"WIREFRAME_DATA\", geometry.id, \"Geometry wireframe 已开启,但没有生成 linePoints。\", geometry);\n }\n if (geometry.style.edge.uvRepeat !== 1 && !geometry.style.wireframe.enabled && !geometry.style.edge.enabled) {\n this.warn(\"UNUSED_UV_REPEAT\", geometry.id, \"uvRepeat 只作用于边框,当前值不会被使用。\", geometry);\n }\n if (geometry.uniformData.length !== 4 || GetInvalidNumberIndex(geometry.uniformData) !== -1) {\n this.error(\"GEOMETRY_UNIFORM\", geometry.id, \"Geometry uniformData 必须包含 4 个有限数。\", geometry);\n }\n }\n\n /**\n * 检查 Mesh 的 Geometry 与 Material 是否能组成目标边框绘制模式。\n * @param mesh 当前 Mesh\n * @returns 无返回值\n */\n public validateMesh(mesh: MeshLike): void {\n const geometry = mesh.data;\n const material = mesh.material;\n\n if (geometry === undefined || material === undefined) {\n this.error(\"MESH_RESOURCE\", mesh.id, \"Mesh 缺少 Geometry 或 Material。\", mesh);\n return;\n }\n\n if (geometry.style.solid.enabled && geometry.type !== \"Rect2d\" && geometry.type !== \"Poly2D\") {\n this.error(\"UNSUPPORTED_SOLID_TYPE\", mesh.id, \"未提供该类型的实体面 Shader。\", mesh);\n }\n if (mesh instanceof IMesh) {\n if (geometry.style.wireframe.enabled && mesh.count > 1) {\n this.warn(\"IMESH_NATIVE_LINES\", mesh.id, \"原生 line-list 使用逐实例顺序回退;只用三角面时才合并为一次绘制。\", mesh);\n }\n for (const raw of mesh.raws.map.values()) {\n if (geometry.type === \"Poly2D\" && raw.style.solid.borderWidth > 0) {\n this.warn(\"POLY_SDF_BORDER\", mesh.id, \"Poly2D 不使用矩形 SDF 边框,请启用 edge 分区。\", mesh);\n }\n if (geometry.type === \"Poly2D\" && (raw.style.join.type !== geometry.style.join.type || raw.style.join.seg !== geometry.style.join.seg)) {\n this.warn(\"IMESH_JOIN_TEMPLATE\", mesh.id, \"连接类型和精度由 Geometry.style.join 决定,Raw 不生成独立拓扑。\", mesh);\n }\n for (const area of [\"solid\", \"wireframe\", \"edge\", \"points\"] as const) {\n if (raw.style[area].enabled && !geometry.style[area].enabled) {\n this.warn(\"IMESH_MISSING_\" + area, mesh.id, \"实例启用了模板未生成的 \" + area + \" 分区;请先配置 Geometry.style。\", mesh);\n }\n }\n for (const area of [\"solid\", \"edge\", \"points\"] as const) {\n if (raw.style[area].addressModeU !== material.style[area].addressModeU\n || raw.style[area].addressModeV !== material.style[area].addressModeV) {\n this.warn(\"IMESH_SAMPLER_\" + area, mesh.id, \"一次绘制共用 Material.style 的采样器,Raw.style 的寻址方式不单独生效。\", mesh);\n }\n }\n if (raw.style.edge.enabled && (raw.style.edge.width !== geometry.style.edge.width\n || raw.style.edge.uvRepeat !== geometry.style.edge.uvRepeat)) {\n this.warn(\"IMESH_EDGE_TEMPLATE\", mesh.id, \"实例实体边框宽度和 UV Repeat 由共用 Geometry.style 生成,Raw 不重建独立几何。\", mesh);\n }\n if (raw.style.points.enabled && (raw.style.points.radius !== geometry.style.points.radius\n || raw.style.points.segments !== geometry.style.points.segments\n || raw.style.points.vertices !== geometry.style.points.vertices\n || raw.style.points.midpoints !== geometry.style.points.midpoints\n || raw.style.points.minPointsLength !== geometry.style.points.minPointsLength\n || raw.style.points.minEdgePointsLength !== geometry.style.points.minEdgePointsLength)) {\n this.warn(\"IMESH_POINTS_TEMPLATE\", mesh.id, \"关键点半径、段数、选择规则由共用 Geometry.style 决定。\", mesh);\n }\n }\n return;\n }\n if (geometry.type === \"Poly2D\" && material.style.solid.borderWidth > 0) {\n this.warn(\"POLY_SDF_BORDER\", mesh.id, \"Poly2D 不使用矩形 SDF 边框,请启用 edge 分区。\", mesh);\n }\n if (geometry.type === \"Poly2D\" && (geometry.style.join.type !== material.style.join.type || geometry.style.join.seg !== material.style.join.seg)) {\n this.warn(\"POLY_JOIN_STYLE\", mesh.id, \"连接拓扑由 Geometry.style.join 生成,建议几何与材质共享 Style。\", mesh);\n }\n for (const area of [\"solid\", \"wireframe\", \"edge\", \"points\"] as const) {\n if (geometry.style[area].enabled !== material.style[area].enabled) {\n this.warn(\"STYLE_ENABLED_\" + area, mesh.id, \"几何与材质的分区开关不同;建议引用同一个 Style。\", mesh);\n }\n }\n if (geometry.style !== material.style && geometry.style.edge.enabled\n && (geometry.style.edge.width !== material.style.edge.width\n || geometry.style.edge.borderAlign !== material.style.edge.borderAlign)) {\n this.warn(\"STYLE_EDGE_PARAMETERS\", mesh.id, \"几何与材质的实体边框参数不同;几何宽度参与生成,建议共享 Style。\", mesh);\n }\n }\n\n /**\n * 检查单个几何缓冲分区。\n * @param geometryId Geometry ID\n * @param name 缓冲分区名\n * @param data CPU 几何数据\n * @param primitiveSize 每个图元使用的索引数\n * @param requireMiterScale 是否要求 miterScale\n * @param source 日志附带对象\n * @returns 无返回值\n */\n private validatePart(\n geometryId: number,\n name: string,\n data: GeoPartDataLike,\n primitiveSize: 2 | 3,\n requireMiterScale: boolean,\n source: object,\n ): void {\n const vertexCount: number = data.geometry.length / 2;\n\n if (data.geometry.length % 2 !== 0) {\n this.error(\"GEOMETRY_VERTEX_\" + name, geometryId, name + \" 顶点数组长度必须是 2 的倍数。\", source);\n }\n if (data.normal.length !== data.geometry.length) {\n this.error(\"GEOMETRY_NORMAL_\" + name, geometryId, name + \" 法线数量必须与顶点数量一致。\", source);\n }\n if (data.uv.length !== data.geometry.length) {\n this.error(\"GEOMETRY_UV_\" + name, geometryId, name + \" UV 数量必须与顶点数量一致。\", source);\n }\n if (data.index.length % primitiveSize !== 0) {\n this.error(\"GEOMETRY_INDEX_COUNT_\" + name, geometryId, name + \" 索引数量与图元类型不匹配。\", source);\n }\n if (data.geometry.length === 0 || data.index.length === 0) {\n this.warn(\"EMPTY_GEOMETRY_\" + name, geometryId, name + \" 不包含可绘制图元。\", source);\n }\n if (\n GetInvalidNumberIndex(data.geometry) !== -1\n || GetInvalidNumberIndex(data.normal) !== -1\n || GetInvalidNumberIndex(data.uv) !== -1\n ) {\n this.error(\"GEOMETRY_NUMBER_\" + name, geometryId, name + \" 包含 NaN 或 Infinity。\", source);\n }\n\n for (const index of data.index) {\n if (index >= vertexCount) {\n this.error(\"GEOMETRY_INDEX_RANGE_\" + name, geometryId, name + \" 存在越界索引 \" + String(index) + \"。\", source);\n break;\n }\n }\n\n if (requireMiterScale && data.miterScale === undefined) {\n this.error(\"GEOMETRY_MITER_MISSING_\" + name, geometryId, name + \" 缺少 miterScale。\", source);\n } else if (data.miterScale !== undefined) {\n if (data.miterScale.length !== vertexCount) {\n this.error(\"GEOMETRY_MITER_COUNT_\" + name, geometryId, name + \" miterScale 数量必须与顶点数量一致。\", source);\n }\n if (GetInvalidNumberIndex(data.miterScale) !== -1) {\n this.error(\"GEOMETRY_MITER_NUMBER_\" + name, geometryId, name + \" miterScale 包含 NaN 或 Infinity。\", source);\n }\n }\n }\n\n /**\n * 输出一次开发模式警告。\n * @param code 问题代码\n * @param objectId 对象 ID\n * @param message 问题说明\n * @param source 关联对象\n * @returns 无返回值\n */\n private warn(code: string, objectId: number, message: string, source: object): void {\n const key: string = \"warn_\" + code + \"_\" + String(objectId);\n\n if (this.reported.has(key)) {\n return;\n }\n\n this.reported.add(key);\n console.warn(\"[BPLineJS:\" + code + \"] \" + message, source);\n }\n\n /**\n * 输出一次开发模式错误。\n * @param code 问题代码\n * @param objectId 对象 ID\n * @param message 问题说明\n * @param source 关联对象\n * @returns 无返回值\n */\n private error(code: string, objectId: number, message: string, source: object): void {\n const key: string = \"error_\" + code + \"_\" + String(objectId);\n\n if (this.reported.has(key)) {\n return;\n }\n\n this.reported.add(key);\n console.error(\"[BPLineJS:\" + code + \"] \" + message, source);\n }\n}\n\n/**\n * 获取数组中首个非有限数索引。\n * @param data 待检查数值数组\n * @returns 非有限数索引,全部有效时返回 -1\n */\nconst GetInvalidNumberIndex = (data: ArrayLike<number>): number => {\n for (let index = 0; index < data.length; index += 1) {\n if (!Number.isFinite(data[index])) {\n return index;\n }\n }\n\n return -1;\n};\n\nexport default DevelopmentValidator;\nexport type { DevelopmentValidatorLike };\n"],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import type Scene from "../Scene";
2
+ import SamplerManager from "./SamplerManager";
3
+ import type { PipelineLike } from "./types";
4
+ interface DrawCallManagerLike {
5
+ prepare(width: number, height: number): void;
6
+ draw(scene: Scene, backgroundColor: GPUColorDict, alphaMode: GPUCanvasAlphaMode): void;
7
+ }
8
+ /**
9
+ * 负责编码 BindGroup、DrawCall 并向 GPUQueue 提交命令。
10
+ * @class
11
+ */
12
+ declare class DrawCallManager implements DrawCallManagerLike {
13
+ private readonly pipeline;
14
+ private readonly samplerManager;
15
+ private readonly depthManager;
16
+ /**
17
+ * 创建 DrawCall 管理器。
18
+ * @param pipeline 当前 Render 的共享管线资源
19
+ * @param samplerManager Sampler 管理器
20
+ */
21
+ constructor(pipeline: PipelineLike, samplerManager: SamplerManager);
22
+ /**
23
+ * warmup 只创建深度附件,不提交绘制。
24
+ * @param width 画布物理像素宽度
25
+ * @param height 画布物理像素高度
26
+ */
27
+ prepare(width: number, height: number): void;
28
+ /**
29
+ * 编码并提交当前场景的基础绘制命令。
30
+ * @param scene 场景对象
31
+ * @param backgroundColor 清空画布使用的 RGBA 颜色
32
+ * @param alphaMode Canvas Alpha 合成方式
33
+ * @returns 无返回值
34
+ */
35
+ draw(scene: Scene, backgroundColor: GPUColorDict, alphaMode: GPUCanvasAlphaMode): void;
36
+ /**
37
+ * 编码单个 Mesh 的资源绑定与索引绘制命令。
38
+ * @param device 当前 WebGPU 设备
39
+ * @param renderPass 当前 RenderPass
40
+ * @param mesh 当前 Mesh
41
+ * @param viewBuffer 相机视图矩阵缓冲
42
+ * @param orthogonalBuffer 相机投影矩阵缓冲
43
+ * @param renderCameraBuffer Render 与 Camera 公共参数缓冲
44
+ * @returns 无返回值
45
+ */
46
+ private drawMesh;
47
+ /**
48
+ * 一次绑定 Mesh 实例样式 Storage 与三种几何的贴图,Sampler 各自独立。
49
+ * @param device 当前 WebGPU 设备
50
+ * @param mesh 当前 Mesh,提供独立实例样式缓冲
51
+ * @param material 当前材质
52
+ * @param pipelineTemplate 当前几何类型对应的 Pipeline 模板
53
+ * @returns 材质 BindGroup,必要资源未就绪时返回 undefined
54
+ */
55
+ private createMaterialBindGroup;
56
+ }
57
+ export default DrawCallManager;
58
+ export type { DrawCallManagerLike };