新增禁用sab功能

This commit is contained in:
YHH
2025-09-28 20:41:23 +08:00
parent dedb91379f
commit de3bfd7551
9 changed files with 319 additions and 17 deletions

View File

@@ -50,6 +50,21 @@ jobs:
- name: Build documentation
run: npm run docs:build
- name: Add COOP/COEP headers for SharedArrayBuffer support
run: |
# Create _headers file for Netlify compatibility
echo "/*" > docs/.vitepress/dist/_headers
echo " Cross-Origin-Embedder-Policy: require-corp" >> docs/.vitepress/dist/_headers
echo " Cross-Origin-Opener-Policy: same-origin" >> docs/.vitepress/dist/_headers
# Create .htaccess file for Apache compatibility (GitHub Pages sometimes uses this)
echo "Header always set Cross-Origin-Embedder-Policy require-corp" > docs/.vitepress/dist/.htaccess
echo "Header always set Cross-Origin-Opener-Policy same-origin" >> docs/.vitepress/dist/.htaccess
# Create a meta tag fallback in a headers.html file that can be included
echo '<meta http-equiv="Cross-Origin-Embedder-Policy" content="require-corp">' > docs/.vitepress/dist/headers.html
echo '<meta http-equiv="Cross-Origin-Opener-Policy" content="same-origin">' >> docs/.vitepress/dist/headers.html
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:

View File

@@ -11370,8 +11370,9 @@ class WorkerEntitySystem extends EntitySystem {
initializeSharedArrayBuffer() {
try {
if (!this.isSharedArrayBufferSupported()) {
console.warn(`[${this.systemName}] SharedArrayBuffer not supported, falling back to traditional Worker mode`);
console.warn(`[${this.systemName}] SharedArrayBuffer not supported, falling back to single Worker mode for collision detection integrity`);
this.config.useSharedArrayBuffer = false;
this.config.workerCount = 1;
return;
}
const bufferSize = this.config.maxEntities * this.config.entityDataSize * 4;
@@ -11379,10 +11380,11 @@ class WorkerEntitySystem extends EntitySystem {
this.sharedFloatArray = new Float32Array(this.sharedBuffer);
console.log(`[${this.systemName}] SharedArrayBuffer initialized successfully (${bufferSize} bytes)`);
} catch (error) {
console.warn(`[${this.systemName}] SharedArrayBuffer init failed, falling back to traditional Worker mode:`, error);
console.warn(`[${this.systemName}] SharedArrayBuffer init failed, falling back to single Worker mode for collision detection integrity:`, error);
this.config.useSharedArrayBuffer = false;
this.sharedBuffer = null;
this.sharedFloatArray = null;
this.config.workerCount = 1;
}
}
/**
@@ -11912,11 +11914,11 @@ let PhysicsWorkerSystem = class extends WorkerEntitySystem {
Matcher.empty().all(Position, Velocity, Physics),
{
enableWorker,
workerCount: navigator.hardwareConcurrency || 2,
// 恢复多Worker
// 当 SharedArrayBuffer 可用时使用多 Worker否则使用单 Worker 保证碰撞检测完整性
workerCount: this.isSharedArrayBufferAvailable() ? navigator.hardwareConcurrency || 2 : 1,
systemConfig: defaultConfig,
useSharedArrayBuffer: true
// 使用SharedArrayBuffer进行全局碰撞检测
// 优先使用 SharedArrayBuffer
}
);
this.physicsConfig = {
@@ -11928,6 +11930,12 @@ let PhysicsWorkerSystem = class extends WorkerEntitySystem {
};
this.startTime = 0;
}
/**
* 检查 SharedArrayBuffer 是否可用
*/
isSharedArrayBufferAvailable() {
return typeof SharedArrayBuffer !== "undefined" && self.crossOriginIsolated;
}
extractEntityData(entity) {
const position = entity.getComponent(Position);
const velocity = entity.getComponent(Velocity);
@@ -12050,6 +12058,45 @@ let PhysicsWorkerSystem = class extends WorkerEntitySystem {
getPhysicsConfig() {
return { ...this.physicsConfig };
}
/**
* 强制禁用 SharedArrayBuffer用于测试降级行为
*/
forceDisableSharedArrayBuffer() {
console.log(`[${this.systemName}] Manually disabling SharedArrayBuffer for testing`);
this.config.useSharedArrayBuffer = false;
this.config.workerCount = 1;
this.sharedBuffer = null;
this.sharedFloatArray = null;
if (this.workerPool) {
this.workerPool.destroy();
this.workerPool = null;
}
if (this.config.enableWorker) {
this.initializeWorkerPool();
}
}
/**
* 获取当前运行状态
*/
getCurrentStatus() {
const workerInfo = this.getWorkerInfo();
let mode = "sync";
if (workerInfo.enabled) {
if (workerInfo.sharedArrayBufferEnabled && workerInfo.sharedArrayBufferSupported) {
mode = "shared-buffer";
} else if (workerInfo.workerCount === 1) {
mode = "single-worker";
} else {
mode = "multi-worker";
}
}
return {
mode,
sharedArrayBufferEnabled: workerInfo.sharedArrayBufferEnabled,
workerCount: workerInfo.workerCount,
workerEnabled: workerInfo.enabled
};
}
/**
* 性能监控
*/
@@ -12470,6 +12517,18 @@ class GameScene extends Scene {
});
}
}
/**
* 切换 SharedArrayBuffer 状态
*/
toggleSharedArrayBuffer() {
this.physicsSystem.forceDisableSharedArrayBuffer();
}
/**
* 获取物理系统状态
*/
getPhysicsSystemStatus() {
return this.physicsSystem.getCurrentStatus();
}
/**
* 获取系统信息
*/
@@ -12533,6 +12592,7 @@ class WorkerDemo {
"entityCount",
"entityCountValue",
"toggleWorker",
"toggleSAB",
"gravity",
"gravityValue",
"friction",
@@ -12547,7 +12607,8 @@ class WorkerDemo {
"physicsTime",
"renderTime",
"frameTime",
"memoryUsage"
"memoryUsage",
"sabStatus"
];
for (const id of elementIds) {
const element = document.getElementById(id);
@@ -12576,6 +12637,12 @@ class WorkerDemo {
this.updateWorkerStatus();
});
}
if (this.elements.toggleSAB) {
this.elements.toggleSAB.addEventListener("click", () => {
this.gameScene.toggleSharedArrayBuffer();
this.updateWorkerStatus();
});
}
if (this.elements.gravity && this.elements.gravityValue) {
const slider = this.elements.gravity;
slider.addEventListener("input", () => {
@@ -12664,6 +12731,7 @@ class WorkerDemo {
const systemInfo = this.gameScene.getSystemInfo();
const workerInfo = systemInfo.physics;
const entityCount = systemInfo.entityCount;
const status = this.gameScene.getPhysicsSystemStatus();
if (this.elements.workerStatus) {
if (workerInfo.enabled) {
this.elements.workerStatus.textContent = `启用 (${workerInfo.workerCount} Workers)`;
@@ -12681,6 +12749,24 @@ class WorkerDemo {
this.elements.workerLoad.textContent = "N/A";
}
}
if (this.elements.sabStatus) {
const modeNames = {
"shared-buffer": "SharedArrayBuffer模式",
"single-worker": "单Worker模式",
"multi-worker": "多Worker模式",
"sync": "同步模式"
};
this.elements.sabStatus.textContent = modeNames[status.mode] || status.mode;
this.elements.sabStatus.className = status.mode === "shared-buffer" ? "worker-enabled" : "worker-disabled";
}
if (this.elements.toggleSAB) {
if (status.sharedArrayBufferEnabled) {
this.elements.toggleSAB.textContent = "禁用 SharedArrayBuffer";
} else {
this.elements.toggleSAB.textContent = "启用 SharedArrayBuffer";
this.elements.toggleSAB.setAttribute("disabled", "true");
}
}
}
getMemoryUsage() {
if ("memory" in performance) {

View File

@@ -118,7 +118,7 @@
color: #ff4a4a;
}
</style>
<script type="module" crossorigin src="/ecs-framework/demos/worker-system/assets/index-DRe9rMYY.js"></script>
<script type="module" crossorigin src="/ecs-framework/demos/worker-system/assets/index-DeXakP9I.js"></script>
</head>
<body>
<div class="container">
@@ -171,6 +171,17 @@
</div>
</div>
<script>
// Register Service Worker for SharedArrayBuffer support on GitHub Pages
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/ecs-framework/sw.js')
.then((registration) => {
console.log('Service Worker registered for SharedArrayBuffer support:', registration);
})
.catch((error) => {
console.log('Service Worker registration failed:', error);
});
}
</script>
</body>
</html>

39
docs/public/sw.js Normal file
View File

@@ -0,0 +1,39 @@
// Service Worker to add COOP/COEP headers for SharedArrayBuffer support
// This is a workaround for GitHub Pages which doesn't support custom headers
self.addEventListener('fetch', (event) => {
// Only handle requests for this origin
if (!event.request.url.startsWith(self.location.origin)) {
return;
}
event.respondWith(
fetch(event.request).then((response) => {
// Clone the response to modify headers
const newResponse = new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: {
...Object.fromEntries(response.headers.entries()),
'Cross-Origin-Embedder-Policy': 'require-corp',
'Cross-Origin-Opener-Policy': 'same-origin'
}
});
return newResponse;
}).catch((error) => {
console.error('Service Worker fetch failed:', error);
return fetch(event.request);
})
);
});
self.addEventListener('install', (event) => {
console.log('Service Worker: Installing for SharedArrayBuffer support');
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
console.log('Service Worker: Activated for SharedArrayBuffer support');
event.waitUntil(self.clients.claim());
});

View File

@@ -138,6 +138,7 @@
<div class="control-group">
<label>Worker 设置:</label>
<button id="toggleWorker">禁用 Worker</button>
<button id="toggleSAB">禁用 SharedArrayBuffer</button>
</div>
<div class="control-group">
@@ -163,6 +164,7 @@
<div class="stat-line">实体数量: <span id="entityCountStat">0</span></div>
<div class="stat-line">Worker状态: <span id="workerStatus" class="worker-disabled">未启用</span></div>
<div class="stat-line">Worker负载: <span id="workerLoad">N/A</span></div>
<div class="stat-line">运行模式: <span id="sabStatus" class="worker-disabled">同步模式</span></div>
<div class="stat-line">物理系统耗时: <span id="physicsTime">0</span>ms</div>
<div class="stat-line">渲染系统耗时: <span id="renderTime">0</span>ms</div>
<div class="stat-line">总帧时间: <span id="frameTime">0</span>ms</div>

View File

@@ -160,6 +160,20 @@ export class GameScene extends Scene {
}
}
/**
* 切换 SharedArrayBuffer 状态
*/
public toggleSharedArrayBuffer(): void {
this.physicsSystem.disableSharedArrayBuffer();
}
/**
* 获取物理系统状态
*/
public getPhysicsSystemStatus() {
return this.physicsSystem.getCurrentStatus();
}
/**
* 获取系统信息
*/

View File

@@ -54,10 +54,10 @@ class WorkerDemo {
private initializeUIElements(): void {
const elementIds = [
'entityCount', 'entityCountValue', 'toggleWorker',
'entityCount', 'entityCountValue', 'toggleWorker', 'toggleSAB',
'gravity', 'gravityValue', 'friction', 'frictionValue', 'spawnParticles',
'clearEntities', 'resetDemo', 'fps', 'entityCountStat', 'workerStatus', 'workerLoad',
'physicsTime', 'renderTime', 'frameTime', 'memoryUsage'
'physicsTime', 'renderTime', 'frameTime', 'memoryUsage', 'sabStatus'
];
for (const id of elementIds) {
@@ -93,6 +93,14 @@ class WorkerDemo {
});
}
// SharedArrayBuffer切换按钮
if (this.elements.toggleSAB) {
this.elements.toggleSAB.addEventListener('click', () => {
this.gameScene.toggleSharedArrayBuffer();
this.updateWorkerStatus();
});
}
// 重力滑块
if (this.elements.gravity && this.elements.gravityValue) {
@@ -249,6 +257,7 @@ class WorkerDemo {
const systemInfo = this.gameScene.getSystemInfo();
const workerInfo = systemInfo.physics;
const entityCount = systemInfo.entityCount;
const status = this.gameScene.getPhysicsSystemStatus();
if (this.elements.workerStatus) {
if (workerInfo.enabled) {
@@ -268,6 +277,29 @@ class WorkerDemo {
this.elements.workerLoad.textContent = 'N/A';
}
}
// 更新 SharedArrayBuffer 状态
if (this.elements.sabStatus) {
const modeNames = {
'shared-buffer': 'SharedArrayBuffer模式',
'single-worker': '单Worker模式',
'multi-worker': '多Worker模式',
'sync': '同步模式'
};
this.elements.sabStatus.textContent = modeNames[status.mode] || status.mode;
this.elements.sabStatus.className = status.mode === 'shared-buffer' ? 'worker-enabled' : 'worker-disabled';
}
// 更新 SharedArrayBuffer 按钮文本
if (this.elements.toggleSAB) {
if (status.sharedArrayBufferEnabled) {
this.elements.toggleSAB.textContent = '禁用 SharedArrayBuffer';
} else {
this.elements.toggleSAB.textContent = '启用 SharedArrayBuffer';
this.elements.toggleSAB.setAttribute('disabled', 'true'); // SAB 一旦禁用就无法重新启用
}
}
}
private getMemoryUsage(): number {

View File

@@ -37,13 +37,17 @@ export class PhysicsWorkerSystem extends WorkerEntitySystem<PhysicsEntityData> {
groundFriction: 0.98
};
// 检查 SharedArrayBuffer 是否可用
const isSharedArrayBufferAvailable = typeof SharedArrayBuffer !== 'undefined' && self.crossOriginIsolated;
super(
Matcher.empty().all(Position, Velocity, Physics),
{
enableWorker,
workerCount: navigator.hardwareConcurrency || 2, // 恢复多Worker
// 当 SharedArrayBuffer 可用时使用多 Worker否则使用单 Worker 保证碰撞检测完整性
workerCount: isSharedArrayBufferAvailable ? (navigator.hardwareConcurrency || 2) : 1,
systemConfig: defaultConfig,
useSharedArrayBuffer: true // 使用SharedArrayBuffer进行全局碰撞检测
useSharedArrayBuffer: true // 优先使用 SharedArrayBuffer
}
);
}
@@ -226,6 +230,49 @@ export class PhysicsWorkerSystem extends WorkerEntitySystem<PhysicsEntityData> {
return { ...this.physicsConfig };
}
/**
* 禁用 SharedArrayBuffer用于测试降级行为
*/
public disableSharedArrayBuffer(): void {
console.log(`[${this.systemName}] Disabling SharedArrayBuffer for testing - falling back to single Worker mode`);
// 使用正式的配置更新 API
this.updateConfig({
useSharedArrayBuffer: false
});
}
/**
* 获取当前运行状态
*/
public getCurrentStatus(): {
mode: 'shared-buffer' | 'single-worker' | 'multi-worker' | 'sync';
sharedArrayBufferEnabled: boolean;
workerCount: number;
workerEnabled: boolean;
} {
const workerInfo = this.getWorkerInfo();
let mode: 'shared-buffer' | 'single-worker' | 'multi-worker' | 'sync' = 'sync';
if (workerInfo.enabled) {
if (workerInfo.sharedArrayBufferEnabled && workerInfo.sharedArrayBufferSupported) {
mode = 'shared-buffer';
} else if (workerInfo.workerCount === 1) {
mode = 'single-worker';
} else {
mode = 'multi-worker';
}
}
return {
mode,
sharedArrayBufferEnabled: workerInfo.sharedArrayBufferEnabled,
workerCount: workerInfo.workerCount,
workerEnabled: workerInfo.enabled
};
}
private startTime: number = 0;
@@ -329,7 +376,7 @@ export class PhysicsWorkerSystem extends WorkerEntitySystem<PhysicsEntityData> {
let y = sharedFloatArray[offset + 2];
let dx = sharedFloatArray[offset + 3];
let dy = sharedFloatArray[offset + 4];
const mass = sharedFloatArray[offset + 5];
// const mass = sharedFloatArray[offset + 5]; // 未使用
const bounce = sharedFloatArray[offset + 6];
const friction = sharedFloatArray[offset + 7];
const radius = sharedFloatArray[offset + 8];

View File

@@ -186,8 +186,8 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
protected config: Required<Omit<WorkerSystemConfig, 'systemConfig'>> & { systemConfig?: any };
private workerPool: WebWorkerPool | null = null;
private isProcessing = false;
private sharedBuffer: SharedArrayBuffer | null = null;
private sharedFloatArray: Float32Array | null = null;
protected sharedBuffer: SharedArrayBuffer | null = null;
protected sharedFloatArray: Float32Array | null = null;
constructor(matcher?: Matcher, config: WorkerSystemConfig = {}) {
super(matcher);
@@ -248,8 +248,10 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
try {
// 检查是否支持SharedArrayBuffer
if (!this.isSharedArrayBufferSupported()) {
console.warn(`[${this.systemName}] SharedArrayBuffer not supported, falling back to traditional Worker mode`);
console.warn(`[${this.systemName}] SharedArrayBuffer not supported, falling back to single Worker mode for collision detection integrity`);
this.config.useSharedArrayBuffer = false;
// 降级到单Worker模式确保所有实体在同一个Worker中处理维持实体间交互的完整性
this.config.workerCount = 1;
return;
}
@@ -261,10 +263,11 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
console.log(`[${this.systemName}] SharedArrayBuffer initialized successfully (${bufferSize} bytes)`);
} catch (error) {
console.warn(`[${this.systemName}] SharedArrayBuffer init failed, falling back to traditional Worker mode:`, error);
console.warn(`[${this.systemName}] SharedArrayBuffer init failed, falling back to single Worker mode for collision detection integrity:`, error);
this.config.useSharedArrayBuffer = false;
this.sharedBuffer = null;
this.sharedFloatArray = null;
this.config.workerCount = 1;
}
}
@@ -633,8 +636,21 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
* 更新Worker配置
*/
public updateConfig(newConfig: Partial<WorkerSystemConfig>): void {
const oldConfig = { ...this.config };
Object.assign(this.config, newConfig);
// 如果 SharedArrayBuffer 设置发生变化,需要重新初始化
if (oldConfig.useSharedArrayBuffer !== this.config.useSharedArrayBuffer) {
this.reinitializeWorkerSystem();
return;
}
// 如果 Worker 数量发生变化,需要重新创建 Worker 池
if (oldConfig.workerCount !== this.config.workerCount) {
this.reinitializeWorkerPool();
return;
}
// 如果禁用Worker清理Worker池
if (!this.config.enableWorker && this.workerPool) {
this.workerPool.destroy();
@@ -647,6 +663,46 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
}
}
/**
* 重新初始化整个 Worker 系统(包括 SharedArrayBuffer
*/
private reinitializeWorkerSystem(): void {
// 清理现有资源
if (this.workerPool) {
this.workerPool.destroy();
this.workerPool = null;
}
this.sharedBuffer = null;
this.sharedFloatArray = null;
// 如果禁用 SharedArrayBuffer降级到单 Worker 模式
if (!this.config.useSharedArrayBuffer) {
this.config.workerCount = 1;
}
// 重新初始化
if (this.config.enableWorker && this.isWorkerSupported()) {
if (this.config.useSharedArrayBuffer) {
this.initializeSharedArrayBuffer();
}
this.initializeWorkerPool();
}
}
/**
* 重新初始化 Worker 池(保持 SharedArrayBuffer
*/
private reinitializeWorkerPool(): void {
if (this.workerPool) {
this.workerPool.destroy();
this.workerPool = null;
}
if (this.config.enableWorker && this.isWorkerSupported()) {
this.initializeWorkerPool();
}
}
/**
* 获取系统性能信息
*/