From dedb91379f5f8a2268d95bf70f828708829819ae Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Sun, 28 Sep 2025 20:22:06 +0800 Subject: [PATCH] =?UTF-8?q?worker=E7=B3=BB=E7=BB=9F=E4=B8=8D=E6=94=AF?= =?UTF-8?q?=E6=8C=81sab=E5=9B=9E=E9=80=80=E5=88=B0=E6=99=AE=E9=80=9Aworker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/public/_headers | 3 + .../{index-83126548.js => index-DRe9rMYY.js} | 111 ++++++++---------- docs/public/demos/worker-system/index.html | 2 +- package.json | 4 +- .../src/ECS/Systems/WorkerEntitySystem.ts | 40 ++++++- scripts/update-worker-demo.js | 73 ++++++++++++ 6 files changed, 164 insertions(+), 69 deletions(-) create mode 100644 docs/public/_headers rename docs/public/demos/worker-system/assets/{index-83126548.js => index-DRe9rMYY.js} (99%) create mode 100644 scripts/update-worker-demo.js diff --git a/docs/public/_headers b/docs/public/_headers new file mode 100644 index 00000000..430bd97c --- /dev/null +++ b/docs/public/_headers @@ -0,0 +1,3 @@ +/* + Cross-Origin-Embedder-Policy: require-corp + Cross-Origin-Opener-Policy: same-origin \ No newline at end of file diff --git a/docs/public/demos/worker-system/assets/index-83126548.js b/docs/public/demos/worker-system/assets/index-DRe9rMYY.js similarity index 99% rename from docs/public/demos/worker-system/assets/index-83126548.js rename to docs/public/demos/worker-system/assets/index-DRe9rMYY.js index 0412b770..867e6c65 100644 --- a/docs/public/demos/worker-system/assets/index-83126548.js +++ b/docs/public/demos/worker-system/assets/index-DRe9rMYY.js @@ -19,16 +19,12 @@ }).observe(document, { childList: true, subtree: true }); function getFetchOpts(link) { const fetchOpts = {}; - if (link.integrity) - fetchOpts.integrity = link.integrity; - if (link.referrerPolicy) - fetchOpts.referrerPolicy = link.referrerPolicy; + if (link.integrity) fetchOpts.integrity = link.integrity; + if (link.referrerPolicy) fetchOpts.referrerPolicy = link.referrerPolicy; if (link.crossOrigin === "use-credentials") fetchOpts.credentials = "include"; - else if (link.crossOrigin === "anonymous") - fetchOpts.credentials = "omit"; - else - fetchOpts.credentials = "same-origin"; + else if (link.crossOrigin === "anonymous") fetchOpts.credentials = "omit"; + else fetchOpts.credentials = "same-origin"; return fetchOpts; } function processPreload(link) { @@ -1077,28 +1073,14 @@ var LogLevel; LogLevel2[LogLevel2["None"] = 5] = "None"; })(LogLevel || (LogLevel = {})); const Colors = { - // 基础颜色 - BLACK: "\x1B[30m", RED: "\x1B[31m", GREEN: "\x1B[32m", YELLOW: "\x1B[33m", - BLUE: "\x1B[34m", - MAGENTA: "\x1B[35m", - CYAN: "\x1B[36m", - WHITE: "\x1B[37m", // 亮色版本 BRIGHT_BLACK: "\x1B[90m", BRIGHT_RED: "\x1B[91m", - BRIGHT_GREEN: "\x1B[92m", - BRIGHT_YELLOW: "\x1B[93m", - BRIGHT_BLUE: "\x1B[94m", - BRIGHT_MAGENTA: "\x1B[95m", - BRIGHT_CYAN: "\x1B[96m", - BRIGHT_WHITE: "\x1B[97m", // 特殊 - RESET: "\x1B[0m", - BOLD: "\x1B[1m", - UNDERLINE: "\x1B[4m" + RESET: "\x1B[0m" }; class ConsoleLogger { constructor(config = {}) { @@ -1403,8 +1385,7 @@ class SoAStorage { const value = instance[key]; const type = typeof value; if (type === "number") { - if (highPrecisionFields.has(key)) - ; + if (highPrecisionFields.has(key)) ; else if (float64Fields.has(key)) { this.fields.set(key, new Float64Array(this._capacity)); } else if (int32Fields.has(key)) { @@ -11388,12 +11369,20 @@ class WorkerEntitySystem extends EntitySystem { */ initializeSharedArrayBuffer() { try { + if (!this.isSharedArrayBufferSupported()) { + console.warn(`[${this.systemName}] SharedArrayBuffer not supported, falling back to traditional Worker mode`); + this.config.useSharedArrayBuffer = false; + return; + } const bufferSize = this.config.maxEntities * this.config.entityDataSize * 4; this.sharedBuffer = new SharedArrayBuffer(bufferSize); this.sharedFloatArray = new Float32Array(this.sharedBuffer); + console.log(`[${this.systemName}] SharedArrayBuffer initialized successfully (${bufferSize} bytes)`); } catch (error) { - console.warn(`[${this.systemName}] SharedArrayBuffer init failed:`, error); + console.warn(`[${this.systemName}] SharedArrayBuffer init failed, falling back to traditional Worker mode:`, error); this.config.useSharedArrayBuffer = false; + this.sharedBuffer = null; + this.sharedFloatArray = null; } } /** @@ -11506,21 +11495,26 @@ class WorkerEntitySystem extends EntitySystem { this.isProcessing = true; try { if (this.config.enableWorker && this.workerPool) { - if (this.config.useSharedArrayBuffer && this.sharedFloatArray) { + if (this.config.useSharedArrayBuffer && this.sharedFloatArray && this.isSharedArrayBufferSupported()) { this.processWithSharedArrayBuffer(entities).finally(() => { this.isProcessing = false; }); } else { + if (this.config.useSharedArrayBuffer) { + console.log(`[${this.systemName}] Falling back to traditional Worker mode for this frame`); + } this.processWithWorker(entities).finally(() => { this.isProcessing = false; }); } } else { + console.log(`[${this.systemName}] Worker not available, processing synchronously`); this.processSynchronously(entities); this.isProcessing = false; } } catch (error) { this.isProcessing = false; + console.error(`[${this.systemName}] Processing failed:`, error); throw error; } } @@ -11666,10 +11660,21 @@ class WorkerEntitySystem extends EntitySystem { * 获取系统性能信息 */ getWorkerInfo() { + let currentMode = "sync"; + if (this.config.enableWorker && this.workerPool) { + if (this.config.useSharedArrayBuffer && this.sharedFloatArray && this.isSharedArrayBufferSupported()) { + currentMode = "shared-buffer"; + } else { + currentMode = "worker"; + } + } return { enabled: this.config.enableWorker, workerCount: this.config.workerCount, - isProcessing: this.isProcessing + isProcessing: this.isProcessing, + sharedArrayBufferSupported: this.isSharedArrayBufferSupported(), + sharedArrayBufferEnabled: this.config.useSharedArrayBuffer, + currentMode }; } /** @@ -11800,15 +11805,12 @@ class WebWorkerPool { this.busyWorkers.clear(); } } -var __defProp$3 = Object.defineProperty; var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor; var __decorateClass$3 = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) - result = (kind ? decorator(target, key, result) : decorator(result)) || result; - if (kind && result) - __defProp$3(target, key, result); + result = decorator(result) || result; return result; }; let Position = class extends Component { @@ -11890,15 +11892,12 @@ let Lifetime = class extends Component { Lifetime = __decorateClass$3([ ECSComponent("Lifetime") ], Lifetime); -var __defProp$2 = Object.defineProperty; var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor; var __decorateClass$2 = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) - result = (kind ? decorator(target, key, result) : decorator(result)) || result; - if (kind && result) - __defProp$2(target, key, result); + result = decorator(result) || result; return result; }; let PhysicsWorkerSystem = class extends WorkerEntitySystem { @@ -12004,8 +12003,7 @@ let PhysicsWorkerSystem = class extends WorkerEntitySystem { const relativeVelocityX = ball2.dx - ball1.dx; const relativeVelocityY = ball2.dy - ball1.dy; const velocityAlongNormal = relativeVelocityX * nx + relativeVelocityY * ny; - if (velocityAlongNormal > 0) - continue; + if (velocityAlongNormal > 0) continue; const restitution = (ball1.bounce + ball2.bounce) * 0.5; const impulseScalar = -(1 + restitution) * velocityAlongNormal / (1 / ball1.mass + 1 / ball2.mass); const impulseX = impulseScalar * nx; @@ -12072,8 +12070,7 @@ let PhysicsWorkerSystem = class extends WorkerEntitySystem { */ writeEntityToBuffer(entityData, offset) { const sharedArray = this.sharedFloatArray; - if (!sharedArray) - return; + if (!sharedArray) return; const currentEntityCount = Math.floor(offset / 9) + 1; sharedArray[0] = currentEntityCount; const dataOffset = offset + 9; @@ -12099,8 +12096,7 @@ let PhysicsWorkerSystem = class extends WorkerEntitySystem { */ readEntityFromBuffer(offset) { const sharedArray = this.sharedFloatArray; - if (!sharedArray) - return null; + if (!sharedArray) return null; const dataOffset = offset + 9; return { id: sharedArray[dataOffset + 0], @@ -12129,8 +12125,7 @@ let PhysicsWorkerSystem = class extends WorkerEntitySystem { for (let i = startIndex; i < endIndex && i < actualEntityCount; i++) { const offset = i * 9 + 9; const id = sharedFloatArray[offset + 0]; - if (id === 0) - continue; + if (id === 0) continue; let x = sharedFloatArray[offset + 1]; let y = sharedFloatArray[offset + 2]; let dx = sharedFloatArray[offset + 3]; @@ -12167,8 +12162,7 @@ let PhysicsWorkerSystem = class extends WorkerEntitySystem { for (let i = startIndex; i < endIndex && i < actualEntityCount; i++) { const offset1 = i * 9 + 9; const id1 = sharedFloatArray[offset1 + 0]; - if (id1 === 0) - continue; + if (id1 === 0) continue; let x1 = sharedFloatArray[offset1 + 1]; let y1 = sharedFloatArray[offset1 + 2]; let dx1 = sharedFloatArray[offset1 + 3]; @@ -12177,12 +12171,10 @@ let PhysicsWorkerSystem = class extends WorkerEntitySystem { const bounce1 = sharedFloatArray[offset1 + 6]; const radius1 = sharedFloatArray[offset1 + 8]; for (let j = 0; j < actualEntityCount; j++) { - if (i === j) - continue; + if (i === j) continue; const offset2 = j * 9 + 9; const id2 = sharedFloatArray[offset2 + 0]; - if (id2 === 0) - continue; + if (id2 === 0) continue; const x2 = sharedFloatArray[offset2 + 1]; const y2 = sharedFloatArray[offset2 + 2]; const dx2 = sharedFloatArray[offset2 + 3]; @@ -12190,8 +12182,7 @@ let PhysicsWorkerSystem = class extends WorkerEntitySystem { const mass2 = sharedFloatArray[offset2 + 5]; const bounce2 = sharedFloatArray[offset2 + 6]; const radius2 = sharedFloatArray[offset2 + 8]; - if (isNaN(x2) || isNaN(y2) || isNaN(radius2) || radius2 <= 0) - continue; + if (isNaN(x2) || isNaN(y2) || isNaN(radius2) || radius2 <= 0) continue; const deltaX = x2 - x1; const deltaY = y2 - y1; const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); @@ -12207,8 +12198,7 @@ let PhysicsWorkerSystem = class extends WorkerEntitySystem { const relativeVelocityX = dx2 - dx1; const relativeVelocityY = dy2 - dy1; const velocityAlongNormal = relativeVelocityX * nx + relativeVelocityY * ny; - if (velocityAlongNormal > 0) - continue; + if (velocityAlongNormal > 0) continue; const restitution = (bounce1 + bounce2) * 0.5; const impulseScalar = -(1 + restitution) * velocityAlongNormal / (1 / mass1 + 1 / mass2); const impulseX = impulseScalar * nx; @@ -12231,15 +12221,12 @@ let PhysicsWorkerSystem = class extends WorkerEntitySystem { PhysicsWorkerSystem = __decorateClass$2([ ECSSystem("PhysicsWorkerSystem") ], PhysicsWorkerSystem); -var __defProp$1 = Object.defineProperty; var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor; var __decorateClass$1 = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) - result = (kind ? decorator(target, key, result) : decorator(result)) || result; - if (kind && result) - __defProp$1(target, key, result); + result = decorator(result) || result; return result; }; let RenderSystem = class extends EntitySystem { @@ -12321,15 +12308,12 @@ let RenderSystem = class extends EntitySystem { RenderSystem = __decorateClass$1([ ECSSystem("RenderSystem") ], RenderSystem); -var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __decorateClass = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) - result = (kind ? decorator(target, key, result) : decorator(result)) || result; - if (kind && result) - __defProp(target, key, result); + result = decorator(result) || result; return result; }; let LifetimeSystem = class extends EntitySystem { @@ -12507,8 +12491,7 @@ class WorkerDemo { this.lastWorkerStatusUpdate = 0; this.elements = {}; this.gameLoop = () => { - if (!this.isRunning) - return; + if (!this.isRunning) return; const currentTime = performance.now(); const deltaTime = (currentTime - this.lastTime) / 1e3; this.lastTime = currentTime; diff --git a/docs/public/demos/worker-system/index.html b/docs/public/demos/worker-system/index.html index cc07b219..8a59c0f5 100644 --- a/docs/public/demos/worker-system/index.html +++ b/docs/public/demos/worker-system/index.html @@ -118,7 +118,7 @@ color: #ff4a4a; } - +
diff --git a/package.json b/package.json index bfc6cbd5..d09ea02c 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,9 @@ "docs:build": "npm run docs:api && vitepress build docs", "docs:preview": "vitepress preview docs", "docs:api": "typedoc", - "docs:api:watch": "typedoc --watch" + "docs:api:watch": "typedoc --watch", + "update:worker-demo": "npm run build:core && cd examples/worker-system-demo && npm run build && cd ../.. && npm run copy:worker-demo", + "copy:worker-demo": "node scripts/update-worker-demo.js" }, "author": "yhh", "license": "MIT", diff --git a/packages/core/src/ECS/Systems/WorkerEntitySystem.ts b/packages/core/src/ECS/Systems/WorkerEntitySystem.ts index 89f80d5c..4e1a2087 100644 --- a/packages/core/src/ECS/Systems/WorkerEntitySystem.ts +++ b/packages/core/src/ECS/Systems/WorkerEntitySystem.ts @@ -246,15 +246,25 @@ export abstract class WorkerEntitySystem extends EntitySystem */ private initializeSharedArrayBuffer(): void { try { + // 检查是否支持SharedArrayBuffer + if (!this.isSharedArrayBufferSupported()) { + console.warn(`[${this.systemName}] SharedArrayBuffer not supported, falling back to traditional Worker mode`); + this.config.useSharedArrayBuffer = false; + return; + } + // 使用配置的实体数据大小和最大实体数量 // 预分配缓冲区:maxEntities * entityDataSize * 4字节 const bufferSize = this.config.maxEntities * this.config.entityDataSize * 4; this.sharedBuffer = new SharedArrayBuffer(bufferSize); this.sharedFloatArray = new Float32Array(this.sharedBuffer); + console.log(`[${this.systemName}] SharedArrayBuffer initialized successfully (${bufferSize} bytes)`); } catch (error) { - console.warn(`[${this.systemName}] SharedArrayBuffer init failed:`, error); + console.warn(`[${this.systemName}] SharedArrayBuffer init failed, falling back to traditional Worker mode:`, error); this.config.useSharedArrayBuffer = false; + this.sharedBuffer = null; + this.sharedFloatArray = null; } } @@ -377,23 +387,31 @@ export abstract class WorkerEntitySystem extends EntitySystem try { if (this.config.enableWorker && this.workerPool) { - if (this.config.useSharedArrayBuffer && this.sharedFloatArray) { + // 检查SharedArrayBuffer是否真正可用 + if (this.config.useSharedArrayBuffer && this.sharedFloatArray && this.isSharedArrayBufferSupported()) { // 使用SharedArrayBuffer优化的异步处理 this.processWithSharedArrayBuffer(entities).finally(() => { this.isProcessing = false; }); } else { + // 如果配置了SharedArrayBuffer但不可用,记录降级信息 + if (this.config.useSharedArrayBuffer) { + console.log(`[${this.systemName}] Falling back to traditional Worker mode for this frame`); + } // 传统Worker异步处理 this.processWithWorker(entities).finally(() => { this.isProcessing = false; }); } } else { + // 同步处理(最后的fallback) + console.log(`[${this.systemName}] Worker not available, processing synchronously`); this.processSynchronously(entities); this.isProcessing = false; } } catch (error) { this.isProcessing = false; + console.error(`[${this.systemName}] Processing failed:`, error); throw error; } } @@ -636,11 +654,27 @@ export abstract class WorkerEntitySystem extends EntitySystem enabled: boolean; workerCount: number; isProcessing: boolean; + sharedArrayBufferSupported: boolean; + sharedArrayBufferEnabled: boolean; + currentMode: 'shared-buffer' | 'worker' | 'sync'; } { + let currentMode: 'shared-buffer' | 'worker' | 'sync' = 'sync'; + + if (this.config.enableWorker && this.workerPool) { + if (this.config.useSharedArrayBuffer && this.sharedFloatArray && this.isSharedArrayBufferSupported()) { + currentMode = 'shared-buffer'; + } else { + currentMode = 'worker'; + } + } + return { enabled: this.config.enableWorker, workerCount: this.config.workerCount, - isProcessing: this.isProcessing + isProcessing: this.isProcessing, + sharedArrayBufferSupported: this.isSharedArrayBufferSupported(), + sharedArrayBufferEnabled: this.config.useSharedArrayBuffer, + currentMode }; } diff --git a/scripts/update-worker-demo.js b/scripts/update-worker-demo.js new file mode 100644 index 00000000..1f2133eb --- /dev/null +++ b/scripts/update-worker-demo.js @@ -0,0 +1,73 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); + +const DEMO_DIST_DIR = 'examples/worker-system-demo/dist'; +const VITEPRESS_DEMO_DIR = 'docs/public/demos/worker-system'; + +function updateWorkerDemo() { + console.log('🔄 更新 Worker System Demo 资源...'); + + try { + // 1. 清理旧的 JS 文件 + const assetsDir = path.join(VITEPRESS_DEMO_DIR, 'assets'); + if (fs.existsSync(assetsDir)) { + const files = fs.readdirSync(assetsDir); + const jsFiles = files.filter(file => file.startsWith('index-') && file.endsWith('.js')); + + for (const jsFile of jsFiles) { + const filePath = path.join(assetsDir, jsFile); + fs.unlinkSync(filePath); + console.log(`🗑️ 删除旧文件: ${jsFile}`); + } + } + + // 2. 复制新的资源文件 + const sourceAssetsDir = path.join(DEMO_DIST_DIR, 'assets'); + if (!fs.existsSync(sourceAssetsDir)) { + throw new Error(`源目录不存在: ${sourceAssetsDir}`); + } + + // 确保目标目录存在 + if (!fs.existsSync(assetsDir)) { + fs.mkdirSync(assetsDir, { recursive: true }); + } + + const sourceFiles = fs.readdirSync(sourceAssetsDir); + const newJsFile = sourceFiles.find(file => file.startsWith('index-') && file.endsWith('.js')); + + if (!newJsFile) { + throw new Error('未找到新的 JS 文件'); + } + + // 复制新的 JS 文件 + const sourcePath = path.join(sourceAssetsDir, newJsFile); + const targetPath = path.join(assetsDir, newJsFile); + fs.copyFileSync(sourcePath, targetPath); + console.log(`📁 复制新文件: ${newJsFile}`); + + // 3. 更新 index.html 中的引用 + const indexHtmlPath = path.join(VITEPRESS_DEMO_DIR, 'index.html'); + if (fs.existsSync(indexHtmlPath)) { + let content = fs.readFileSync(indexHtmlPath, 'utf-8'); + + // 更新 script 标签中的文件名 + const scriptRegex = /src="\/ecs-framework\/demos\/worker-system\/assets\/index-[^"]+\.js"/; + const newScriptSrc = `/ecs-framework/demos/worker-system/assets/${newJsFile}`; + content = content.replace(scriptRegex, `src="${newScriptSrc}"`); + + fs.writeFileSync(indexHtmlPath, content); + console.log(`📝 更新 index.html 引用: ${newJsFile}`); + } + + console.log('✅ Worker System Demo 资源更新完成!'); + console.log('💡 提示:运行 npm run docs:build 来重新构建文档'); + + } catch (error) { + console.error('❌ 更新失败:', error.message); + process.exit(1); + } +} + +updateWorkerDemo(); \ No newline at end of file