import { IComponentDebugData } from '../../Types'; import { Core } from '../../Core'; /** * 组件数据收集器 */ export class ComponentDataCollector { private static componentSizeCache = new Map(); /** * 收集组件数据(轻量版,不计算实际内存大小) */ public collectComponentData(): IComponentDebugData { const scene = Core.scene; if (!scene) { return { componentTypes: 0, componentInstances: 0, componentStats: [] }; } const entityList = (scene as any).entities; if (!entityList?.buffer) { return { componentTypes: 0, componentInstances: 0, componentStats: [] }; } const componentStats = new Map(); let totalInstances = 0; entityList.buffer.forEach((entity: any) => { if (entity.components) { entity.components.forEach((component: any) => { const typeName = component.constructor.name; const stats = componentStats.get(typeName) || { count: 0, entities: 0 }; stats.count++; totalInstances++; componentStats.set(typeName, stats); }); } }); // 获取池利用率信息 let poolUtilizations = new Map(); let poolSizes = new Map(); try { const { ComponentPoolManager } = require('../../ECS/Core/ComponentPool'); const poolManager = ComponentPoolManager.getInstance(); const poolStats = poolManager.getPoolStats(); const utilizations = poolManager.getPoolUtilization(); for (const [typeName, stats] of poolStats.entries()) { poolSizes.set(typeName, stats.maxSize); } for (const [typeName, util] of utilizations.entries()) { poolUtilizations.set(typeName, util.utilization); } } catch (error) { // 如果无法获取池信息,使用默认值 } return { componentTypes: componentStats.size, componentInstances: totalInstances, componentStats: Array.from(componentStats.entries()).map(([typeName, stats]) => { const poolSize = poolSizes.get(typeName) || 0; const poolUtilization = poolUtilizations.get(typeName) || 0; // 使用预估的基础内存大小,避免每帧计算 const memoryPerInstance = this.getEstimatedComponentSize(typeName); return { typeName, instanceCount: stats.count, memoryPerInstance: memoryPerInstance, totalMemory: stats.count * memoryPerInstance, poolSize: poolSize, poolUtilization: poolUtilization, averagePerEntity: stats.count / entityList.buffer.length }; }) }; } /** * 获取组件类型的估算内存大小(基于预设值,不进行实际计算) */ private getEstimatedComponentSize(typeName: string): number { if (ComponentDataCollector.componentSizeCache.has(typeName)) { return ComponentDataCollector.componentSizeCache.get(typeName)!; } const scene = Core.scene; if (!scene) return 64; const entityList = (scene as any).entities; if (!entityList?.buffer) return 64; let calculatedSize = 64; try { for (const entity of entityList.buffer) { if (entity.components) { const component = entity.components.find((c: any) => c.constructor.name === typeName); if (component) { calculatedSize = this.calculateQuickObjectSize(component); break; } } } } catch (error) { calculatedSize = 64; } ComponentDataCollector.componentSizeCache.set(typeName, calculatedSize); return calculatedSize; } private calculateQuickObjectSize(obj: any): number { if (!obj || typeof obj !== 'object') return 8; let size = 32; const visited = new WeakSet(); const calculate = (item: any, depth: number = 0): number => { if (!item || typeof item !== 'object' || visited.has(item) || depth > 3) { return 0; } visited.add(item); let itemSize = 0; try { const keys = Object.keys(item); for (let i = 0; i < Math.min(keys.length, 20); i++) { const key = keys[i]; if (key === 'entity' || key === '_entity' || key === 'constructor') continue; const value = item[key]; itemSize += key.length * 2; if (typeof value === 'string') { itemSize += Math.min(value.length * 2, 200); } else if (typeof value === 'number') { itemSize += 8; } else if (typeof value === 'boolean') { itemSize += 4; } else if (typeof value === 'object' && value !== null) { itemSize += calculate(value, depth + 1); } } } catch (error) { return 32; } return itemSize; }; size += calculate(obj); return Math.max(size, 32); } /** * 为内存快照功能提供的详细内存计算 * 只在用户主动请求内存快照时调用 */ public calculateDetailedComponentMemory(typeName: string): number { const scene = Core.scene; if (!scene) return this.getEstimatedComponentSize(typeName); const entityList = (scene as any).entities; if (!entityList?.buffer) return this.getEstimatedComponentSize(typeName); try { // 找到第一个包含此组件的实体,分析组件大小 for (const entity of entityList.buffer) { if (entity.components) { const component = entity.components.find((c: any) => c.constructor.name === typeName); if (component) { return this.estimateObjectSize(component); } } } } catch (error) { // 忽略错误,使用估算值 } return this.getEstimatedComponentSize(typeName); } /** * 估算对象内存大小(仅用于内存快照) * 优化版本:减少递归深度,提高性能 */ private estimateObjectSize(obj: any, visited = new WeakSet(), depth = 0): number { if (obj === null || obj === undefined || depth > 10) return 0; if (visited.has(obj)) return 0; let size = 0; const type = typeof obj; switch (type) { case 'boolean': size = 4; break; case 'number': size = 8; break; case 'string': size = 24 + Math.min(obj.length * 2, 1000); break; case 'object': visited.add(obj); if (Array.isArray(obj)) { size = 40 + (obj.length * 8); const maxElements = Math.min(obj.length, 50); for (let i = 0; i < maxElements; i++) { size += this.estimateObjectSize(obj[i], visited, depth + 1); } } else { size = 32; try { const ownKeys = Object.getOwnPropertyNames(obj); const maxProps = Math.min(ownKeys.length, 30); for (let i = 0; i < maxProps; i++) { const key = ownKeys[i]; if (key === 'constructor' || key === '__proto__' || key === 'entity' || key === '_entity' || key.startsWith('_cc_') || key.startsWith('__')) { continue; } try { size += 16 + (key.length * 2); const value = obj[key]; if (value !== undefined && value !== null) { size += this.estimateObjectSize(value, visited, depth + 1); } } catch (error) { continue; } } } catch (error) { size = 128; } } break; default: size = 8; } return Math.ceil(size / 8) * 8; } public static clearCache(): void { ComponentDataCollector.componentSizeCache.clear(); } }