@zephyr3d/device 0.2.2 → 0.2.4

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/pool.js ADDED
@@ -0,0 +1,352 @@
1
+ /**
2
+ * ObjectPool class is responsible for managing and reusing textures and framebuffers.
3
+ * @public
4
+ */ class Pool {
5
+ /** @internal */ _memCost;
6
+ /** @internal */ _memCostThreshold;
7
+ /** @internal */ _device;
8
+ /** @internal */ _freeTextures = {};
9
+ /** @internal */ _allocatedTextures = new WeakMap();
10
+ /** @internal */ _autoReleaseTextures = new Set();
11
+ /** @internal */ _freeFramebuffers = {};
12
+ /** @internal */ _allocatedFramebuffers = new WeakMap();
13
+ /** @internal */ _autoReleaseFramebuffers = new Set();
14
+ /**
15
+ * Creates an instance of Pool class
16
+ * @param device - Rendering device
17
+ */ constructor(device, memCostThreshold = 1024 * 1024 * 1024){
18
+ this._device = device;
19
+ this._memCost = 0;
20
+ this._memCostThreshold = memCostThreshold;
21
+ this._freeTextures = {};
22
+ this._allocatedTextures = new WeakMap();
23
+ this._autoReleaseTextures = new Set();
24
+ this._freeFramebuffers = {};
25
+ this._allocatedFramebuffers = new WeakMap();
26
+ this._autoReleaseFramebuffers = new Set();
27
+ this._memCost = 0;
28
+ }
29
+ autoRelease() {
30
+ // auto release objects
31
+ for (const tex of this._autoReleaseTextures){
32
+ this.releaseTexture(tex);
33
+ }
34
+ this._autoReleaseTextures.clear();
35
+ for (const fb of this._autoReleaseFramebuffers){
36
+ this.releaseFrameBuffer(fb);
37
+ }
38
+ this._autoReleaseFramebuffers.clear();
39
+ // Free up video memory if memory usage is greater than specific value
40
+ if (this._memCost >= this._memCostThreshold) {
41
+ this.purge();
42
+ }
43
+ }
44
+ /**
45
+ * Fetch a temporal 2D texture from the object pool.
46
+ * @param autoRelease - Whether the texture should be automatically released at the next frame.
47
+ * @param format - The format of the texture.
48
+ * @param width - The width of the texture.
49
+ * @param height - The height of the texture.
50
+ * @param mipmapping - Whether this texture support mipmapping
51
+ * @returns The fetched Texture2D object.
52
+ */ fetchTemporalTexture2D(autoRelease, format, width, height, mipmapping = false) {
53
+ const hash = `2d:${format}:${width}:${height}:${mipmapping ? 1 : 0}`;
54
+ let texture = null;
55
+ const list = this._freeTextures[hash];
56
+ if (!list) {
57
+ texture = this._device.createTexture2D(format, width, height, mipmapping ? {} : {
58
+ samplerOptions: {
59
+ mipFilter: 'none'
60
+ }
61
+ });
62
+ this._memCost += texture.memCost;
63
+ } else {
64
+ texture = list.pop();
65
+ if (list.length === 0) {
66
+ this._freeTextures[hash] = undefined;
67
+ }
68
+ }
69
+ this._allocatedTextures.set(texture, {
70
+ hash,
71
+ refcount: 1,
72
+ dispose: false
73
+ });
74
+ if (autoRelease) {
75
+ this._autoReleaseTextures.add(texture);
76
+ }
77
+ return texture;
78
+ }
79
+ /**
80
+ * Fetch a temporal 2D array texture from the object pool.
81
+ * @param autoRelease - Whether the texture should be automatically released at the next frame.
82
+ * @param format - Format of the texture.
83
+ * @param width - Width of the texture.
84
+ * @param height - Height of the texture.
85
+ * @param numLayers - Layer count of the texture
86
+ * @param mipmapping - Whether this texture support mipmapping
87
+ * @returns The fetched Texture2DArray object.
88
+ */ fetchTemporalTexture2DArray(autoRelease, format, width, height, numLayers, mipmapping = false) {
89
+ const hash = `2darray:${format}:${width}:${height}:${numLayers}:${mipmapping ? 1 : 0}`;
90
+ let texture = null;
91
+ const list = this._freeTextures[hash];
92
+ if (!list) {
93
+ texture = this._device.createTexture2DArray(format, width, height, numLayers, mipmapping ? {} : {
94
+ samplerOptions: {
95
+ mipFilter: 'none'
96
+ }
97
+ });
98
+ this._memCost += texture.memCost;
99
+ } else {
100
+ texture = list.pop();
101
+ if (list.length === 0) {
102
+ this._freeTextures[hash] = undefined;
103
+ }
104
+ }
105
+ this._allocatedTextures.set(texture, {
106
+ hash,
107
+ refcount: 1,
108
+ dispose: false
109
+ });
110
+ if (autoRelease) {
111
+ this._autoReleaseTextures.add(texture);
112
+ }
113
+ return texture;
114
+ }
115
+ /**
116
+ * Fetch a temporal Cube texture from the object pool.
117
+ * @param autoRelease - Whether the texture should be automatically released at the next frame.
118
+ * @param format - Format of the texture.
119
+ * @param size - size of the texture.
120
+ * @param mipmapping - Whether this texture support mipmapping
121
+ * @returns The fetched TextureCube object.
122
+ */ fetchTemporalTextureCube(autoRelease, format, size, mipmapping = false) {
123
+ const hash = `cube:${format}:${size}:${mipmapping ? 1 : 0}`;
124
+ let texture = null;
125
+ const list = this._freeTextures[hash];
126
+ if (!list) {
127
+ texture = this._device.createCubeTexture(format, size, mipmapping ? {} : {
128
+ samplerOptions: {
129
+ mipFilter: 'none'
130
+ }
131
+ });
132
+ this._memCost += texture.memCost;
133
+ } else {
134
+ texture = list.pop();
135
+ if (list.length === 0) {
136
+ this._freeTextures[hash] = undefined;
137
+ }
138
+ }
139
+ this._allocatedTextures.set(texture, {
140
+ hash,
141
+ refcount: 1,
142
+ dispose: false
143
+ });
144
+ if (autoRelease) {
145
+ this._autoReleaseTextures.add(texture);
146
+ }
147
+ return texture;
148
+ }
149
+ /**
150
+ * Creates a temporal framebuffer from the object pool.
151
+ * @param autoRelease - Whether the framebuffer should be automatically released at the next frame.
152
+ * @param width - Width of the framebuffer
153
+ * @param height - Height of the framebuffer
154
+ * @param colorAttachments - Array of color attachments or texture format of the framebuffer.
155
+ * @param depthAttachment - Depth attachment or texture format of the framebuffer.
156
+ * @param mipmapping - Whether mipmapping should be enabled when creating color attachment textures, default is false.
157
+ * @param sampleCount - The sample count for the framebuffer, default is 1.
158
+ * @param ignoreDepthStencil - Whether to ignore depth stencil when resolving msaa framebuffer, default is true.
159
+ * @param attachmentMipLevel - The mipmap level to which the color attachment will render, default is 0
160
+ * @param attachmentCubeface - The cubemap face to which the color attachment will render, default is 0
161
+ * @param attachmentLayer - The texture layer to which the color attachment will render, default is 0
162
+ * @returns The fetched FrameBuffer object.
163
+ */ fetchTemporalFramebuffer(autoRelease, width, height, colorTexOrFormat, depthTexOrFormat = null, mipmapping = false, sampleCount = 1, ignoreDepthStencil = true, attachmentMipLevel = 0, attachmentCubeface = 0, attachmentLayer = 0) {
164
+ const colors = Array.isArray(colorTexOrFormat) ? colorTexOrFormat : colorTexOrFormat ? [
165
+ colorTexOrFormat
166
+ ] : [];
167
+ const colorAttachments = colors.map((val)=>{
168
+ return typeof val === 'string' ? this.fetchTemporalTexture2D(false, val, width, height, mipmapping) : val;
169
+ });
170
+ const depthAttachment = typeof depthTexOrFormat === 'string' ? this.fetchTemporalTexture2D(false, depthTexOrFormat, width, height, false) : depthTexOrFormat;
171
+ const fb = this.createTemporalFramebuffer(autoRelease, colorAttachments, depthAttachment, sampleCount, ignoreDepthStencil, attachmentMipLevel, attachmentCubeface, attachmentLayer);
172
+ for(let i = 0; i < colors.length; i++){
173
+ if (typeof colors[i] === 'string') {
174
+ this.releaseTexture(colorAttachments[i]);
175
+ }
176
+ }
177
+ if (typeof depthTexOrFormat === 'string') {
178
+ this.releaseTexture(depthAttachment);
179
+ }
180
+ return fb;
181
+ }
182
+ /**
183
+ * Creates a temporal framebuffer from the object pool.
184
+ * @param autoRelease - Whether the framebuffer should be automatically released at the next frame.
185
+ * @param colorAttachments - Array of color attachments for the framebuffer.
186
+ * @param depthAttachment - Depth attachment for the framebuffer.
187
+ * @param sampleCount - The sample count for the framebuffer, default is 1.
188
+ * @param ignoreDepthStencil - Whether to ignore depth stencil when resolving msaa framebuffer, default is true.
189
+ * @param attachmentMipLevel - The mipmap level to which the color attachment will render, default is 0.
190
+ * @param attachmentCubeface - The cubemap face to which the color attachment will render, default is 0.
191
+ * @param attachmentLayer - The texture layer to which the color attachment will render, default is 0.
192
+ * @returns The fetched FrameBuffer object.
193
+ */ createTemporalFramebuffer(autoRelease, colorAttachments, depthAttachment = null, sampleCount = 1, ignoreDepthStencil = true, attachmentMipLevel = 0, attachmentCubeface = 0, attachmentLayer = 0) {
194
+ colorAttachments = colorAttachments ?? [];
195
+ let hash = `${depthAttachment?.uid ?? 0}:${sampleCount ?? 1}:${ignoreDepthStencil ? 1 : 0}`;
196
+ if (colorAttachments.length > 0) {
197
+ hash += `:${attachmentMipLevel}:${attachmentCubeface}:${attachmentLayer}`;
198
+ for (const tex of colorAttachments){
199
+ hash += `:${tex.uid}`;
200
+ }
201
+ }
202
+ let fb = null;
203
+ const list = this._freeFramebuffers[hash];
204
+ if (!list) {
205
+ fb = this._device.createFrameBuffer(colorAttachments, depthAttachment, {
206
+ ignoreDepthStencil,
207
+ sampleCount
208
+ });
209
+ for(let i = 0; i < fb.getColorAttachments().length; i++){
210
+ fb.setColorAttachmentMipLevel(i, attachmentMipLevel);
211
+ fb.setColorAttachmentCubeFace(i, attachmentCubeface);
212
+ fb.setColorAttachmentLayer(i, attachmentLayer);
213
+ }
214
+ } else {
215
+ fb = list.pop();
216
+ if (list.length === 0) {
217
+ this._freeFramebuffers[hash] = undefined;
218
+ }
219
+ }
220
+ // Mark referenced textures
221
+ const info = this._allocatedTextures.get(depthAttachment);
222
+ if (info) {
223
+ info.refcount++;
224
+ }
225
+ for (const tex of colorAttachments){
226
+ const info = this._allocatedTextures.get(tex);
227
+ if (info) {
228
+ info.refcount++;
229
+ }
230
+ }
231
+ this._allocatedFramebuffers.set(fb, hash);
232
+ if (autoRelease) {
233
+ this._autoReleaseFramebuffers.add(fb);
234
+ }
235
+ return fb;
236
+ }
237
+ /**
238
+ * Dispose a texture that is allocated from the object pool.
239
+ * @param texture - The texture to dispose.
240
+ */ disposeTexture(texture) {
241
+ this.safeReleaseTexture(texture, true);
242
+ }
243
+ /**
244
+ * Release a texture back to the object pool.
245
+ * @param texture - The texture to release.
246
+ */ releaseTexture(texture) {
247
+ const info = this._allocatedTextures.get(texture);
248
+ if (!info) {
249
+ console.error(`ObjectPool.releaseTexture(): texture is not allocated from pool`);
250
+ } else {
251
+ this.safeReleaseTexture(texture);
252
+ }
253
+ }
254
+ /**
255
+ * Dispose a framebuffer that is allocated from the object pool.
256
+ * @param fb - The framebuffer to dispose.
257
+ */ disposeFrameBuffer(fb) {
258
+ const hash = this._allocatedFramebuffers.get(fb);
259
+ if (!hash) {
260
+ console.error(`ObjectPool.disposeFrameBuffer(): framebuffer is not allocated from pool`);
261
+ } else {
262
+ this.internalDisposeFrameBuffer(fb);
263
+ fb.dispose();
264
+ }
265
+ }
266
+ /**
267
+ * Release a framebuffer back to the object pool.
268
+ * @param fb - The framebuffer to release.
269
+ */ releaseFrameBuffer(fb) {
270
+ const hash = this._allocatedFramebuffers.get(fb);
271
+ if (!hash) {
272
+ console.error(`ObjectPool.releaseFrameBuffer(): framebuffer is not allocated from pool`);
273
+ } else {
274
+ this.internalDisposeFrameBuffer(fb);
275
+ const list = this._freeFramebuffers[hash];
276
+ if (list) {
277
+ list.push(fb);
278
+ } else {
279
+ this._freeFramebuffers[hash] = [
280
+ fb
281
+ ];
282
+ }
283
+ }
284
+ }
285
+ /**
286
+ * Purge the object pool by disposing all free framebuffers and textures.
287
+ */ purge() {
288
+ for(const k in this._freeFramebuffers){
289
+ const list = this._freeFramebuffers[k];
290
+ if (list) {
291
+ for (const fb of this._freeFramebuffers[k]){
292
+ this.internalDisposeFrameBuffer(fb);
293
+ fb.dispose();
294
+ }
295
+ }
296
+ }
297
+ this._freeFramebuffers = {};
298
+ for(const k in this._freeTextures){
299
+ const list = this._freeTextures[k];
300
+ for (const tex of list){
301
+ this._memCost -= tex.memCost;
302
+ tex.dispose();
303
+ }
304
+ }
305
+ this._freeTextures = {};
306
+ }
307
+ /** @internal */ internalDisposeFrameBuffer(fb) {
308
+ if (fb) {
309
+ // Release attachment textures
310
+ const colorAttachments = fb.getColorAttachments();
311
+ if (colorAttachments) {
312
+ for (const tex of colorAttachments){
313
+ this.safeReleaseTexture(tex);
314
+ }
315
+ }
316
+ const depthAttachment = fb.getDepthAttachment();
317
+ if (depthAttachment) {
318
+ this.safeReleaseTexture(depthAttachment);
319
+ }
320
+ this._allocatedFramebuffers.delete(fb);
321
+ this._autoReleaseFramebuffers.delete(fb);
322
+ }
323
+ }
324
+ /** @internal */ safeReleaseTexture(texture, purge = false) {
325
+ const info = this._allocatedTextures.get(texture);
326
+ if (info) {
327
+ info.refcount--;
328
+ if (info.refcount === 0) {
329
+ this._allocatedTextures.delete(texture);
330
+ this._autoReleaseTextures.delete(texture);
331
+ if (purge || info.dispose) {
332
+ this._memCost -= texture.memCost;
333
+ texture.dispose();
334
+ } else {
335
+ const list = this._freeTextures[info.hash];
336
+ if (list) {
337
+ list.push(texture);
338
+ } else {
339
+ this._freeTextures[info.hash] = [
340
+ texture
341
+ ];
342
+ }
343
+ }
344
+ } else if (purge) {
345
+ info.dispose = true;
346
+ }
347
+ }
348
+ }
349
+ }
350
+
351
+ export { Pool };
352
+ //# sourceMappingURL=pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zephyr3d/device",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Device API for zephyr3d",
5
5
  "homepage": "https://github.com/gavinyork/zephyr3d#readme",
6
6
  "type": "module",
@@ -44,7 +44,6 @@
44
44
  "@rollup/plugin-terser": "^0.4.0",
45
45
  "@rollup/pluginutils": "^5.0.2",
46
46
  "@swc/core": "^1.3.62",
47
- "@zephyr3d/base": "^0.1.3",
48
47
  "cross-env": "^7.0.3",
49
48
  "rollup": "^3.15.0",
50
49
  "rollup-plugin-copy": "^3.4.0",
@@ -54,12 +53,10 @@
54
53
  "shx": "^0.3.4",
55
54
  "typescript": "^5.1.3"
56
55
  },
57
- "peerDependencies": {
58
- "@zephyr3d/base": "^0.1.3"
59
- },
60
56
  "dependencies": {
61
- "@types/node": "^18.13.0",
62
- "@webgpu/types": "^0.1.40"
57
+ "@zephyr3d/base": "^0.1.5",
58
+ "@webgpu/types": "^0.1.40",
59
+ "@types/node": "^18.13.0"
63
60
  },
64
61
  "scripts": {
65
62
  "clean": "shx rm -rf ./dist .tsbuildinfo",