更新池利用率
This commit is contained in:
@@ -38,6 +38,8 @@ interface DetailedDebugInfo {
|
|||||||
minFrameTime: number;
|
minFrameTime: number;
|
||||||
maxFrameTime: number;
|
maxFrameTime: number;
|
||||||
frameTimeHistory: number[];
|
frameTimeHistory: number[];
|
||||||
|
engineFrameTime: number;
|
||||||
|
ecsPercentage: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 内存信息
|
// 内存信息
|
||||||
@@ -100,6 +102,7 @@ interface DetailedDebugInfo {
|
|||||||
memoryUsage: number;
|
memoryUsage: number;
|
||||||
updateOrder: number;
|
updateOrder: number;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
percentage: number;
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -254,6 +257,16 @@ class ECSDebugServer {
|
|||||||
private transformToDetailedDebugInfo(instance: GameInstance, rawData: any): DetailedDebugInfo {
|
private transformToDetailedDebugInfo(instance: GameInstance, rawData: any): DetailedDebugInfo {
|
||||||
const uptime = (Date.now() - instance.connectTime) / 1000;
|
const uptime = (Date.now() - instance.connectTime) / 1000;
|
||||||
|
|
||||||
|
// 计算系统性能数据,包括ECS占比
|
||||||
|
const systemBreakdown = rawData.performance?.systemBreakdown || [];
|
||||||
|
const systemPerformance = rawData.performance?.systemPerformance || [];
|
||||||
|
|
||||||
|
// 创建系统名称到占比的映射
|
||||||
|
const systemPercentageMap = new Map<string, number>();
|
||||||
|
systemBreakdown.forEach((sys: any) => {
|
||||||
|
systemPercentageMap.set(sys.systemName, sys.percentage || 0);
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
instanceId: instance.id,
|
instanceId: instance.id,
|
||||||
instanceName: instance.name,
|
instanceName: instance.name,
|
||||||
@@ -268,7 +281,9 @@ class ECSDebugServer {
|
|||||||
averageFrameTime: rawData.performance?.averageFrameTime || rawData.performance?.frameTime || 0,
|
averageFrameTime: rawData.performance?.averageFrameTime || rawData.performance?.frameTime || 0,
|
||||||
minFrameTime: rawData.performance?.minFrameTime || rawData.performance?.frameTime || 0,
|
minFrameTime: rawData.performance?.minFrameTime || rawData.performance?.frameTime || 0,
|
||||||
maxFrameTime: rawData.performance?.maxFrameTime || rawData.performance?.frameTime || 0,
|
maxFrameTime: rawData.performance?.maxFrameTime || rawData.performance?.frameTime || 0,
|
||||||
frameTimeHistory: rawData.performance?.frameTimeHistory || []
|
frameTimeHistory: rawData.performance?.frameTimeHistory || [],
|
||||||
|
engineFrameTime: rawData.performance?.engineFrameTime || 0,
|
||||||
|
ecsPercentage: rawData.performance?.ecsPercentage || 0
|
||||||
},
|
},
|
||||||
|
|
||||||
memory: {
|
memory: {
|
||||||
@@ -307,8 +322,12 @@ class ECSDebugServer {
|
|||||||
|
|
||||||
systems: {
|
systems: {
|
||||||
total: rawData.systems?.totalSystems || 0,
|
total: rawData.systems?.totalSystems || 0,
|
||||||
systemStats: (rawData.systems?.systemsInfo || []).map((sys: any) => ({
|
systemStats: (rawData.systems?.systemsInfo || []).map((sys: any) => {
|
||||||
name: sys.name,
|
const systemName = sys.name;
|
||||||
|
const percentage = systemPercentageMap.get(systemName) || 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: systemName,
|
||||||
type: sys.type || 'Unknown',
|
type: sys.type || 'Unknown',
|
||||||
entityCount: sys.entityCount || 0,
|
entityCount: sys.entityCount || 0,
|
||||||
averageExecutionTime: sys.executionTime || 0,
|
averageExecutionTime: sys.executionTime || 0,
|
||||||
@@ -317,8 +336,10 @@ class ECSDebugServer {
|
|||||||
executionTimeHistory: sys.executionTimeHistory || [],
|
executionTimeHistory: sys.executionTimeHistory || [],
|
||||||
memoryUsage: sys.memoryUsage || 0,
|
memoryUsage: sys.memoryUsage || 0,
|
||||||
updateOrder: sys.updateOrder || 0,
|
updateOrder: sys.updateOrder || 0,
|
||||||
enabled: sys.enabled !== false
|
enabled: sys.enabled !== false,
|
||||||
}))
|
percentage: percentage
|
||||||
|
};
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
scenes: {
|
scenes: {
|
||||||
@@ -348,7 +369,9 @@ const defaultDebugInfo: DetailedDebugInfo = {
|
|||||||
averageFrameTime: 0,
|
averageFrameTime: 0,
|
||||||
minFrameTime: 0,
|
minFrameTime: 0,
|
||||||
maxFrameTime: 0,
|
maxFrameTime: 0,
|
||||||
frameTimeHistory: []
|
frameTimeHistory: [],
|
||||||
|
engineFrameTime: 0,
|
||||||
|
ecsPercentage: 0
|
||||||
},
|
},
|
||||||
memory: {
|
memory: {
|
||||||
totalMemory: 0,
|
totalMemory: 0,
|
||||||
@@ -425,6 +448,7 @@ module.exports = Editor.Panel.define({
|
|||||||
const isAutoRefresh = ref(true);
|
const isAutoRefresh = ref(true);
|
||||||
const refreshInterval = ref(100);
|
const refreshInterval = ref(100);
|
||||||
const lastUpdateTime = ref('');
|
const lastUpdateTime = ref('');
|
||||||
|
const showComponentPoolHelp = ref(false);
|
||||||
|
|
||||||
let intervalId: NodeJS.Timeout | null = null;
|
let intervalId: NodeJS.Timeout | null = null;
|
||||||
let debugServer: ECSDebugServer | null = null;
|
let debugServer: ECSDebugServer | null = null;
|
||||||
@@ -556,6 +580,84 @@ module.exports = Editor.Panel.define({
|
|||||||
return 'critical';
|
return 'critical';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 打开文档链接
|
||||||
|
const openDocumentation = (section: string): void => {
|
||||||
|
const urls: Record<string, string> = {
|
||||||
|
'component-pool': 'https://github.com/esengine/ecs-framework/tree/master/docs/component-design-guide.md#1-对象池优化',
|
||||||
|
'performance-optimization': 'https://github.com/esengine/ecs-framework/tree/master/docs/performance-optimization.md'
|
||||||
|
};
|
||||||
|
|
||||||
|
const url = urls[section];
|
||||||
|
if (!url) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 在Cocos Creator扩展环境中,直接使用Electron的shell模块
|
||||||
|
const { shell } = require('electron');
|
||||||
|
shell.openExternal(url);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('无法打开链接:', error);
|
||||||
|
// 如果失败,复制到剪贴板
|
||||||
|
copyUrlToClipboard(url);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 复制链接到剪贴板的辅助函数
|
||||||
|
const copyUrlToClipboard = (url: string): void => {
|
||||||
|
try {
|
||||||
|
// 尝试使用现代的剪贴板API
|
||||||
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
navigator.clipboard.writeText(url).then(() => {
|
||||||
|
console.log(`文档链接已复制到剪贴板: ${url}`);
|
||||||
|
// 如果可能的话,显示用户友好的提示
|
||||||
|
if (typeof alert !== 'undefined') {
|
||||||
|
alert(`文档链接已复制到剪贴板,请在浏览器中粘贴访问:\n${url}`);
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
fallbackCopyText(url);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fallbackCopyText(url);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
fallbackCopyText(url);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 备用的复制文本方法
|
||||||
|
const fallbackCopyText = (text: string): void => {
|
||||||
|
try {
|
||||||
|
// 创建临时的文本区域
|
||||||
|
const textArea = document.createElement('textarea');
|
||||||
|
textArea.value = text;
|
||||||
|
textArea.style.position = 'fixed';
|
||||||
|
textArea.style.left = '-999999px';
|
||||||
|
textArea.style.top = '-999999px';
|
||||||
|
document.body.appendChild(textArea);
|
||||||
|
textArea.focus();
|
||||||
|
textArea.select();
|
||||||
|
|
||||||
|
const successful = document.execCommand('copy');
|
||||||
|
document.body.removeChild(textArea);
|
||||||
|
|
||||||
|
if (successful) {
|
||||||
|
console.log(`文档链接已复制到剪贴板: ${text}`);
|
||||||
|
if (typeof alert !== 'undefined') {
|
||||||
|
alert(`文档链接已复制到剪贴板,请在浏览器中粘贴访问:\n${text}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`请手动复制文档链接: ${text}`);
|
||||||
|
if (typeof alert !== 'undefined') {
|
||||||
|
alert(`请手动复制文档链接:\n${text}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`请手动访问文档: ${text}`);
|
||||||
|
if (typeof alert !== 'undefined') {
|
||||||
|
alert(`请手动访问文档:\n${text}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 组件挂载时初始化
|
// 组件挂载时初始化
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await initializeServer();
|
await initializeServer();
|
||||||
@@ -575,6 +677,7 @@ module.exports = Editor.Panel.define({
|
|||||||
isAutoRefresh,
|
isAutoRefresh,
|
||||||
refreshInterval,
|
refreshInterval,
|
||||||
lastUpdateTime,
|
lastUpdateTime,
|
||||||
|
showComponentPoolHelp,
|
||||||
manualRefresh,
|
manualRefresh,
|
||||||
toggleAutoRefresh,
|
toggleAutoRefresh,
|
||||||
changeRefreshInterval,
|
changeRefreshInterval,
|
||||||
@@ -584,7 +687,8 @@ module.exports = Editor.Panel.define({
|
|||||||
getFpsColor,
|
getFpsColor,
|
||||||
getMemoryColor,
|
getMemoryColor,
|
||||||
getECSTimeColor,
|
getECSTimeColor,
|
||||||
getExecutionTimeColor
|
getExecutionTimeColor,
|
||||||
|
openDocumentation
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
template: readFileSync(join(__dirname, '../../../static/template/debug/index.html'), 'utf-8'),
|
template: readFileSync(join(__dirname, '../../../static/template/debug/index.html'), 'utf-8'),
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
background: var(--color-normal-fill);
|
background: var(--color-normal-fill);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
.debug-container {
|
.debug-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
background: var(--color-normal-fill);
|
background: var(--color-normal-fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -660,10 +660,12 @@ ui-button ui-icon {
|
|||||||
/* ECS调试面板样式 */
|
/* ECS调试面板样式 */
|
||||||
.debug-panel {
|
.debug-panel {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
padding-bottom: 32px;
|
||||||
background: var(--color-normal-fill);
|
background: var(--color-normal-fill);
|
||||||
color: var(--color-normal-contrast);
|
color: var(--color-normal-contrast);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1242,7 +1244,7 @@ ui-button ui-icon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.debug-panel::-webkit-scrollbar-thumb:hover {
|
.debug-panel::-webkit-scrollbar-thumb:hover {
|
||||||
background: var(--color-normal-contrast-weaken);
|
background-color: var(--color-focus-border-emphasis);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 百分比条样式 */
|
/* 百分比条样式 */
|
||||||
@@ -1261,7 +1263,340 @@ ui-button ui-icon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.percentage-fill {
|
.percentage-fill {
|
||||||
|
background: linear-gradient(90deg, var(--color-primary), var(--color-primary-emphasis));
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: linear-gradient(90deg, #4CAF50 0%, #FF9800 50%, #F44336 100%);
|
border-radius: inherit;
|
||||||
transition: width 0.3s ease;
|
transition: width 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 调试面板滚动条样式 */
|
||||||
|
.debug-panel::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-panel::-webkit-scrollbar-track {
|
||||||
|
background: var(--color-normal-fill-weaker);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-panel::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--color-normal-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-panel::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--color-focus-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
帮助面板和文档链接样式
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
.section-help {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
background: var(--color-normal-fill);
|
||||||
|
border: 1px solid var(--color-normal-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--color-default-contrast);
|
||||||
|
font-size: 11px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-btn:hover {
|
||||||
|
background: var(--color-normal-fill-emphasis);
|
||||||
|
border-color: var(--color-focus-border);
|
||||||
|
color: var(--color-focus-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-btn ui-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-panel {
|
||||||
|
margin: 16px 0;
|
||||||
|
padding: 20px;
|
||||||
|
background: var(--color-normal-fill);
|
||||||
|
border: 1px solid var(--color-normal-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
animation: fadeInDown 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-content h4 {
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
color: var(--color-focus-contrast);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-content p {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
color: var(--color-default-contrast);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-steps {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-step {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--color-normal-fill-emphasis);
|
||||||
|
border-radius: 6px;
|
||||||
|
border-left: 3px solid var(--color-focus-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-step:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-step h5 {
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
color: var(--color-focus-contrast);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-step p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-default-contrast);
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-example {
|
||||||
|
background: var(--color-normal-fill);
|
||||||
|
border: 1px solid var(--color-normal-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 12px;
|
||||||
|
margin: 8px 0 0 0;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: var(--color-focus-border);
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-links {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 16px;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px solid var(--color-normal-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: var(--color-focus-fill);
|
||||||
|
border: 1px solid var(--color-focus-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--color-focus-contrast);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-link:hover {
|
||||||
|
background: var(--color-focus-fill-emphasis);
|
||||||
|
border-color: var(--color-focus-border-emphasis);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-link ui-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-access-note {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
background: var(--color-info-fill);
|
||||||
|
border: 1px solid var(--color-info-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--color-info-contrast);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-access-note ui-icon {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-info-border);
|
||||||
|
margin-top: 1px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
组件池相关样式
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
.help-tooltip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 4px;
|
||||||
|
cursor: help;
|
||||||
|
color: var(--color-normal-contrast-weaker);
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-tooltip:hover {
|
||||||
|
color: var(--color-focus-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-tooltip ui-icon {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.not-configured {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 4px;
|
||||||
|
color: var(--color-warn-border);
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
.not-configured ui-icon {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-pool-hint {
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--color-normal-contrast-weaker);
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pool-suggestion {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
background: linear-gradient(135deg, var(--color-warn-fill) 0%, var(--color-focus-fill) 100%);
|
||||||
|
border: 1px solid var(--color-warn-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
animation: fadeInUp 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion-content {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion-icon {
|
||||||
|
color: var(--color-warn-border);
|
||||||
|
font-size: 20px;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion-text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion-text h4 {
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
color: var(--color-focus-contrast);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion-text p {
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
color: var(--color-default-contrast);
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion-action {
|
||||||
|
padding: 6px 12px;
|
||||||
|
background: var(--color-focus-border);
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: white;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion-action:hover {
|
||||||
|
background: var(--color-focus-border-emphasis);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
响应式设计增强
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.help-links {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-link {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion-content {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion-icon {
|
||||||
|
align-self: center;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.help-panel {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-step {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-example {
|
||||||
|
font-size: 9px;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-btn {
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-help {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -188,7 +188,68 @@
|
|||||||
<div class="debug-section" v-if="debugInfo.instanceId">
|
<div class="debug-section" v-if="debugInfo.instanceId">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h2>🧩 组件分析</h2>
|
<h2>🧩 组件分析</h2>
|
||||||
|
<div class="section-help">
|
||||||
|
<button class="help-btn" @click="showComponentPoolHelp = !showComponentPoolHelp" title="组件对象池使用说明">
|
||||||
|
<ui-icon value="help"></ui-icon>
|
||||||
|
对象池使用说明
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 对象池使用说明 -->
|
||||||
|
<div v-if="showComponentPoolHelp" class="help-panel">
|
||||||
|
<div class="help-content">
|
||||||
|
<h4>📖 组件对象池使用指南</h4>
|
||||||
|
<p>组件对象池用于复用组件实例,减少频繁创建/销毁组件带来的内存分配开销。当前利用率为0表示还未配置对象池。</p>
|
||||||
|
|
||||||
|
<div class="help-steps">
|
||||||
|
<div class="help-step">
|
||||||
|
<h5>1. 注册组件对象池</h5>
|
||||||
|
<pre class="code-example">import { ComponentPoolManager } from '@esengine/ecs-framework';
|
||||||
|
|
||||||
|
// 为Transform组件注册对象池
|
||||||
|
ComponentPoolManager.getInstance().registerPool(
|
||||||
|
'Transform',
|
||||||
|
() => new Transform(),
|
||||||
|
(comp) => comp.reset(), // 可选的重置函数
|
||||||
|
100 // 池大小
|
||||||
|
);</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="help-step">
|
||||||
|
<h5>2. 使用对象池获取组件</h5>
|
||||||
|
<pre class="code-example">// 从对象池获取组件实例
|
||||||
|
const poolManager = ComponentPoolManager.getInstance();
|
||||||
|
const transform = poolManager.acquireComponent('Transform');
|
||||||
|
|
||||||
|
// 使用完毕后释放回池中
|
||||||
|
poolManager.releaseComponent('Transform', transform);</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="help-step">
|
||||||
|
<h5>3. 查看性能改进</h5>
|
||||||
|
<p>正确配置后,利用率栏将显示池的使用情况。利用率越高说明对象池被有效使用,可以减少GC压力。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="help-links">
|
||||||
|
<a href="#" @click="openDocumentation('component-pool')" class="doc-link">
|
||||||
|
<ui-icon value="book"></ui-icon>
|
||||||
|
查看详细文档
|
||||||
|
</a>
|
||||||
|
<a href="#" @click="openDocumentation('performance-optimization')" class="doc-link">
|
||||||
|
<ui-icon value="speed"></ui-icon>
|
||||||
|
性能优化指南
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="doc-access-note">
|
||||||
|
<ui-icon value="info"></ui-icon>
|
||||||
|
<span>如果无法直接打开文档链接,链接将自动复制到剪贴板,请在浏览器中粘贴访问</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="component-overview">
|
<div class="component-overview">
|
||||||
<div class="overview-item">
|
<div class="overview-item">
|
||||||
<span class="label">组件类型:</span>
|
<span class="label">组件类型:</span>
|
||||||
@@ -206,18 +267,45 @@
|
|||||||
<div class="col-count">实例数</div>
|
<div class="col-count">实例数</div>
|
||||||
<div class="col-memory">内存占用</div>
|
<div class="col-memory">内存占用</div>
|
||||||
<div class="col-pool">对象池</div>
|
<div class="col-pool">对象池</div>
|
||||||
<div class="col-efficiency">利用率</div>
|
<div class="col-efficiency">
|
||||||
|
利用率
|
||||||
|
<span class="help-tooltip" title="对象池利用率:使用中的实例 / 池总大小。利用率为0表示未配置对象池">
|
||||||
|
<ui-icon value="help"></ui-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-for="comp in debugInfo.components.componentStats" :key="comp.typeName" class="table-row">
|
<div v-for="comp in debugInfo.components.componentStats" :key="comp.typeName" class="table-row">
|
||||||
<div class="col-name">{{ comp.typeName }}</div>
|
<div class="col-name">{{ comp.typeName }}</div>
|
||||||
<div class="col-count">{{ comp.instanceCount }}</div>
|
<div class="col-count">{{ comp.instanceCount }}</div>
|
||||||
<div class="col-memory">{{ formatMemory(comp.totalMemory) }}</div>
|
<div class="col-memory">{{ formatMemory(comp.totalMemory) }}</div>
|
||||||
<div class="col-pool">{{ comp.poolSize }}</div>
|
<div class="col-pool">
|
||||||
|
{{ comp.poolSize || '未配置' }}
|
||||||
|
<span v-if="comp.poolSize === 0" class="not-configured" title="点击上方说明了解如何配置对象池">
|
||||||
|
<ui-icon value="warning"></ui-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div class="col-efficiency">
|
<div class="col-efficiency">
|
||||||
<div class="efficiency-bar">
|
<div class="efficiency-bar">
|
||||||
<div class="efficiency-fill" :style="{ width: comp.poolUtilization + '%' }"></div>
|
<div class="efficiency-fill" :style="{ width: comp.poolUtilization + '%' }"></div>
|
||||||
</div>
|
</div>
|
||||||
{{ comp.poolUtilization.toFixed(1) }}%
|
{{ comp.poolUtilization.toFixed(1) }}%
|
||||||
|
<span v-if="comp.poolUtilization === 0 && comp.poolSize === 0" class="no-pool-hint">
|
||||||
|
(无对象池)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 无对象池组件提示 -->
|
||||||
|
<div v-if="debugInfo.components.componentStats.length > 0 && debugInfo.components.componentStats.every(c => c.poolSize === 0)" class="pool-suggestion">
|
||||||
|
<div class="suggestion-content">
|
||||||
|
<ui-icon value="lightbulb" class="suggestion-icon"></ui-icon>
|
||||||
|
<div class="suggestion-text">
|
||||||
|
<h4>💡 性能优化建议</h4>
|
||||||
|
<p>检测到所有组件都未配置对象池。为频繁创建/销毁的组件配置对象池可以显著提升性能,减少垃圾回收压力。</p>
|
||||||
|
<button @click="showComponentPoolHelp = true" class="suggestion-action">
|
||||||
|
了解如何配置对象池
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -149,4 +149,38 @@ export class ComponentPoolManager {
|
|||||||
}
|
}
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取池利用率信息(用于调试)
|
||||||
|
*/
|
||||||
|
getPoolUtilization(): Map<string, { used: number; total: number; utilization: number }> {
|
||||||
|
const utilization = new Map();
|
||||||
|
for (const [name, pool] of this.pools) {
|
||||||
|
const available = pool.getAvailableCount();
|
||||||
|
const maxSize = pool.getMaxSize();
|
||||||
|
const used = maxSize - available;
|
||||||
|
const utilRate = maxSize > 0 ? (used / maxSize * 100) : 0;
|
||||||
|
|
||||||
|
utilization.set(name, {
|
||||||
|
used: used,
|
||||||
|
total: maxSize,
|
||||||
|
utilization: utilRate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return utilization;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定组件的池利用率
|
||||||
|
*/
|
||||||
|
getComponentUtilization(componentName: string): number {
|
||||||
|
const pool = this.pools.get(componentName);
|
||||||
|
if (!pool) return 0;
|
||||||
|
|
||||||
|
const available = pool.getAvailableCount();
|
||||||
|
const maxSize = pool.getMaxSize();
|
||||||
|
const used = maxSize - available;
|
||||||
|
|
||||||
|
return maxSize > 0 ? (used / maxSize * 100) : 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -386,6 +386,12 @@ export interface IPerformanceDebugData {
|
|||||||
samples: number;
|
samples: number;
|
||||||
percentage?: number; // 系统占ECS总时间的百分比
|
percentage?: number; // 系统占ECS总时间的百分比
|
||||||
}>;
|
}>;
|
||||||
|
/** 系统占比分析数据 */
|
||||||
|
systemBreakdown?: Array<{
|
||||||
|
systemName: string;
|
||||||
|
executionTime: number;
|
||||||
|
percentage: number;
|
||||||
|
}>;
|
||||||
/** 内存分配详情 */
|
/** 内存分配详情 */
|
||||||
memoryDetails?: {
|
memoryDetails?: {
|
||||||
entities: number;
|
entities: number;
|
||||||
|
|||||||
@@ -41,6 +41,14 @@ export class DebugReporter {
|
|||||||
this.config = config;
|
this.config = config;
|
||||||
this.sceneStartTime = Date.now();
|
this.sceneStartTime = Date.now();
|
||||||
|
|
||||||
|
// 确保性能监控器在调试模式下被启用
|
||||||
|
if (this.config.enabled && this.config.channels.performance) {
|
||||||
|
if (!this.core._performanceMonitor.isEnabled) {
|
||||||
|
this.core._performanceMonitor.enable();
|
||||||
|
console.log('[ECS Debug] Performance monitor enabled for debugging');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.config.enabled) {
|
if (this.config.enabled) {
|
||||||
this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
@@ -80,6 +88,14 @@ export class DebugReporter {
|
|||||||
|
|
||||||
this.config = newConfig;
|
this.config = newConfig;
|
||||||
|
|
||||||
|
// 根据配置启用或禁用性能监控器
|
||||||
|
if (newConfig.enabled && newConfig.channels.performance) {
|
||||||
|
if (!this.core._performanceMonitor.isEnabled) {
|
||||||
|
this.core._performanceMonitor.enable();
|
||||||
|
console.log('[ECS Debug] Performance monitor enabled for debugging');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!newConfig.enabled && wasEnabled) {
|
if (!newConfig.enabled && wasEnabled) {
|
||||||
this.stop();
|
this.stop();
|
||||||
} else if (newConfig.enabled && (!wasEnabled || urlChanged)) {
|
} else if (newConfig.enabled && (!wasEnabled || urlChanged)) {
|
||||||
@@ -429,6 +445,7 @@ export class DebugReporter {
|
|||||||
maxFrameTime: maxECSTime, // ECS最长执行时间
|
maxFrameTime: maxECSTime, // ECS最长执行时间
|
||||||
frameTimeHistory: [...this.frameTimeHistory],
|
frameTimeHistory: [...this.frameTimeHistory],
|
||||||
systemPerformance: this.getSystemPerformance(),
|
systemPerformance: this.getSystemPerformance(),
|
||||||
|
systemBreakdown: ecsPerformanceData.systemBreakdown,
|
||||||
memoryDetails: this.getMemoryDetails()
|
memoryDetails: this.getMemoryDetails()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -439,6 +456,14 @@ export class DebugReporter {
|
|||||||
private getECSPerformanceData(): { totalExecutionTime: number; systemBreakdown: Array<any> } {
|
private getECSPerformanceData(): { totalExecutionTime: number; systemBreakdown: Array<any> } {
|
||||||
const monitor = this.core._performanceMonitor;
|
const monitor = this.core._performanceMonitor;
|
||||||
if (!monitor) {
|
if (!monitor) {
|
||||||
|
console.warn('[ECS Debug] Performance monitor not found');
|
||||||
|
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!monitor.isEnabled) {
|
||||||
|
console.warn('[ECS Debug] Performance monitor is disabled. Enable it to see ECS performance data.');
|
||||||
|
// 尝试启用性能监控器
|
||||||
|
monitor.enable();
|
||||||
return { totalExecutionTime: 0, systemBreakdown: [] };
|
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,6 +473,11 @@ export class DebugReporter {
|
|||||||
|
|
||||||
const stats = monitor.getAllSystemStats();
|
const stats = monitor.getAllSystemStats();
|
||||||
|
|
||||||
|
if (stats.size === 0) {
|
||||||
|
console.log('[ECS Debug] No system performance data available yet. This is normal on first frames.');
|
||||||
|
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||||||
|
}
|
||||||
|
|
||||||
// 计算各系统的执行时间
|
// 计算各系统的执行时间
|
||||||
for (const [systemName, stat] of stats.entries()) {
|
for (const [systemName, stat] of stats.entries()) {
|
||||||
const systemTime = stat.averageTime || 0;
|
const systemTime = stat.averageTime || 0;
|
||||||
@@ -468,11 +498,14 @@ export class DebugReporter {
|
|||||||
// 按执行时间排序
|
// 按执行时间排序
|
||||||
systemBreakdown.sort((a, b) => b.executionTime - a.executionTime);
|
systemBreakdown.sort((a, b) => b.executionTime - a.executionTime);
|
||||||
|
|
||||||
|
console.log(`[ECS Debug] Performance data: ${stats.size} systems, total time: ${totalTime.toFixed(2)}ms`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
totalExecutionTime: totalTime,
|
totalExecutionTime: totalTime,
|
||||||
systemBreakdown: systemBreakdown
|
systemBreakdown: systemBreakdown
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('[ECS Debug] Error getting ECS performance data:', error);
|
||||||
return { totalExecutionTime: 0, systemBreakdown: [] };
|
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -544,29 +577,56 @@ export class DebugReporter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// 如果entityManager不存在,尝试从场景直接获取
|
||||||
|
const entityList = (scene as any).entities;
|
||||||
|
if (entityList?.buffer) {
|
||||||
|
entityList.buffer.forEach((entity: any) => {
|
||||||
|
entityMemory += this.estimateObjectSize(entity);
|
||||||
|
|
||||||
|
if (entity.components) {
|
||||||
|
entity.components.forEach((component: any) => {
|
||||||
|
componentMemory += this.estimateObjectSize(component);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算系统内存(估算)
|
// 计算系统内存
|
||||||
const entitySystems = (scene as any).entitySystems;
|
const entitySystems = (scene as any).entitySystems;
|
||||||
if (entitySystems?.systems) {
|
if (entitySystems?.systems) {
|
||||||
entitySystems.systems.forEach((system: any) => {
|
entitySystems.systems.forEach((system: any) => {
|
||||||
systemMemory += this.estimateObjectSize(system);
|
systemMemory += this.estimateObjectSize(system);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// 尝试从entityProcessors获取
|
||||||
|
const entityProcessors = (scene as any).entityProcessors;
|
||||||
|
if (entityProcessors?.processors) {
|
||||||
|
entityProcessors.processors.forEach((system: any) => {
|
||||||
|
systemMemory += this.estimateObjectSize(system);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算对象池内存(估算)
|
// 计算对象池内存
|
||||||
try {
|
try {
|
||||||
const poolManager = this.core._poolManager;
|
const { ComponentPoolManager } = require('../ECS/Core/ComponentPool');
|
||||||
if (poolManager) {
|
const poolManager = ComponentPoolManager.getInstance();
|
||||||
// 简单估算对象池内存
|
const poolStats = poolManager.getPoolStats();
|
||||||
pooledMemory = 1024 * 1024; // 1MB估算值
|
|
||||||
|
for (const [typeName, stats] of poolStats.entries()) {
|
||||||
|
// 估算每个组件实例的大小
|
||||||
|
const estimatedComponentSize = this.calculateComponentMemorySize(typeName);
|
||||||
|
pooledMemory += stats.available * estimatedComponentSize;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 忽略对象池内存计算错误
|
// 如果无法访问ComponentPoolManager,使用估算值
|
||||||
|
pooledMemory = 512 * 1024; // 512KB估算值
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取浏览器内存信息
|
// 获取浏览器内存信息
|
||||||
let totalMemory = 512 * 1024 * 1024; // 默认512MB
|
let totalMemory = 512 * 1024 * 1024;
|
||||||
let usedMemory = entityMemory + componentMemory + systemMemory + pooledMemory;
|
let usedMemory = entityMemory + componentMemory + systemMemory + pooledMemory;
|
||||||
let gcCollections = 0;
|
let gcCollections = 0;
|
||||||
|
|
||||||
@@ -637,37 +697,48 @@ export class DebugReporter {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 获取池利用率信息
|
||||||
|
let poolUtilizations = new Map<string, number>();
|
||||||
|
let poolSizes = new Map<string, number>();
|
||||||
|
|
||||||
|
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 {
|
return {
|
||||||
componentTypes: componentStats.size,
|
componentTypes: componentStats.size,
|
||||||
componentInstances: totalInstances,
|
componentInstances: totalInstances,
|
||||||
componentStats: Array.from(componentStats.entries()).map(([typeName, stats]) => {
|
componentStats: Array.from(componentStats.entries()).map(([typeName, stats]) => {
|
||||||
const poolSize = this.getComponentPoolSize(typeName);
|
const poolSize = poolSizes.get(typeName) || 0;
|
||||||
|
const poolUtilization = poolUtilizations.get(typeName) || 0;
|
||||||
const memoryPerInstance = this.calculateComponentMemorySize(typeName);
|
const memoryPerInstance = this.calculateComponentMemorySize(typeName);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
typeName,
|
typeName,
|
||||||
instanceCount: stats.count,
|
instanceCount: stats.count,
|
||||||
memoryPerInstance: memoryPerInstance,
|
memoryPerInstance: memoryPerInstance,
|
||||||
totalMemory: stats.count * memoryPerInstance,
|
totalMemory: stats.count * memoryPerInstance,
|
||||||
poolSize: poolSize,
|
poolSize: poolSize,
|
||||||
poolUtilization: poolSize > 0 ? (stats.count / poolSize * 100) : 0,
|
poolUtilization: poolUtilization,
|
||||||
averagePerEntity: stats.count / entityList.buffer.length
|
averagePerEntity: stats.count / entityList.buffer.length
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取组件池大小
|
|
||||||
*/
|
|
||||||
private getComponentPoolSize(typeName: string): number {
|
|
||||||
try {
|
|
||||||
const poolManager = this.core._poolManager;
|
|
||||||
return (poolManager as any).getPoolSize?.(typeName) || 0;
|
|
||||||
} catch (error) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算组件实际内存大小
|
* 计算组件实际内存大小
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user