Root cause: torch.load() reads 6.9GB .ckpt into Python heap + model params in CPU RAM = ~14GB peak, exceeding 16GB system RAM → OOM Killer. Fix 1 - mmap=True on all torch.load() calls (torch 2.7 supports this): With mmap, checkpoint storage is file-backed (not heap). Only the model parameters (also ~7GB) exist in physical RAM during loading. Peak RAM drops from ~14GB to ~7GB — within safe limits on 16GB machines. Files changed: pipelines.py, hunyuan3ddit.py, model.py (×2), flow_matching_sit.py Fix 2 - malloc_trim(0) after every gc.collect(): Forces glibc to return freed heap pages to OS immediately, so Python's memory pool doesn't hoard freed model memory before the next load. Fix 3 - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True: Prevents CUDA allocator fragmentation between model switches. Fix 4 - Adaptive threshold recalculated: With mmap loading, loading a model requires ~7.5GB (model params) not 14GB. CPU offload threshold lowered from 16GB → 10.5GB, enabling fast path on machines with more headroom. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
321 lines
12 KiB
HTML
321 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
|
|
<head>
|
|
<!-- Import the component -->
|
|
<script src="https://cdn.jsdelivr.net/npm/@google/model-viewer@3.1.1/dist/model-viewer.min.js"
|
|
type="module"></script>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
font-family: Arial, sans-serif;
|
|
}
|
|
|
|
.centered-container {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.modelviewer-panel-button {
|
|
height: 30px;
|
|
margin: 4px 4px;
|
|
padding: 0px 14px;
|
|
background: white;
|
|
border-radius: 10px;
|
|
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25);
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.modelviewer-panel-button.checked {
|
|
background: #6567C9;
|
|
color: white;
|
|
}
|
|
|
|
.modelviewer-panel-button:hover {
|
|
background-color: #e2e6ea;
|
|
}
|
|
|
|
.modelviewer-panel-button-container {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
}
|
|
|
|
.centered-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="centered-container">
|
|
<div class="centered-container">
|
|
<div class="column is-mobile is-centered">
|
|
<model-viewer id="modelviewer" style="height: 600px; width: 500px;"
|
|
rotation-per-second="10deg"
|
|
src="./textured_mesh.glb/" disable-tap
|
|
environment-image="neutral"
|
|
camera-target="0m 0m 0m"
|
|
camera-orbit="0deg 90deg 12m"
|
|
orientation="0deg 0deg 0deg"
|
|
shadow-intensity=".9"
|
|
ar auto-rotate
|
|
camera-controls>
|
|
</model-viewer>
|
|
</div>
|
|
|
|
<div class="modelviewer-panel-button-container">
|
|
<div id="appearance-button" class="modelviewer-panel-button small checked" onclick="showTexture()">
|
|
Appearance
|
|
</div>
|
|
<div id="geometry-button" class="modelviewer-panel-button small" onclick="hideTexture()">Geometry</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let modelViewer;
|
|
let window_state = {
|
|
isModelLoaded: false,
|
|
isHidden: false,
|
|
savedMaterials: null,
|
|
savedExposure: null
|
|
};
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
modelViewer = document.getElementById('modelviewer');
|
|
|
|
// 等待模型加载完成
|
|
modelViewer.addEventListener('load', () => {
|
|
console.log('Model loaded, materials available:', modelViewer.model.materials.length);
|
|
window_state.isModelLoaded = true;
|
|
|
|
// 调试:打印材质信息
|
|
modelViewer.model.materials.forEach((mat, idx) => {
|
|
console.log(`Material ${idx}:`, {
|
|
name: mat.name,
|
|
hasBaseColor: !!mat.pbrMetallicRoughness.baseColorTexture,
|
|
hasMetallicRoughness: !!mat.pbrMetallicRoughness.metallicRoughnessTexture,
|
|
hasNormal: !!mat.normalTexture
|
|
});
|
|
});
|
|
});
|
|
|
|
modelViewer.addEventListener('error', (event) => {
|
|
console.error('Model loading error:', event);
|
|
});
|
|
});
|
|
|
|
function ensureModelLoaded() {
|
|
if (!window_state.isModelLoaded || !modelViewer.model || !modelViewer.model.materials) {
|
|
console.warn('Model not loaded yet');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function saveMaterialsState() {
|
|
if (!ensureModelLoaded()) return false;
|
|
|
|
console.log('Saving materials state...');
|
|
window_state.savedMaterials = [];
|
|
window_state.savedExposure = modelViewer.exposure;
|
|
|
|
for (let i = 0; i < modelViewer.model.materials.length; i++) {
|
|
const material = modelViewer.model.materials[i];
|
|
const pbr = material.pbrMetallicRoughness;
|
|
|
|
const materialState = {
|
|
baseColorTexture: null,
|
|
metallicRoughnessTexture: null,
|
|
normalTexture: null,
|
|
baseColorFactor: null,
|
|
metallicFactor: null,
|
|
roughnessFactor: null
|
|
};
|
|
|
|
// 保存纹理引用
|
|
try {
|
|
if (pbr.baseColorTexture && pbr.baseColorTexture.texture) {
|
|
materialState.baseColorTexture = pbr.baseColorTexture.texture;
|
|
console.log(`Saved baseColorTexture for material ${i}`);
|
|
}
|
|
if (pbr.metallicRoughnessTexture && pbr.metallicRoughnessTexture.texture) {
|
|
materialState.metallicRoughnessTexture = pbr.metallicRoughnessTexture.texture;
|
|
console.log(`Saved metallicRoughnessTexture for material ${i}`);
|
|
}
|
|
if (material.normalTexture && material.normalTexture.texture) {
|
|
materialState.normalTexture = material.normalTexture.texture;
|
|
console.log(`Saved normalTexture for material ${i}`);
|
|
}
|
|
|
|
// 保存材质参数
|
|
materialState.baseColorFactor = [...pbr.baseColorFactor];
|
|
materialState.metallicFactor = pbr.metallicFactor;
|
|
materialState.roughnessFactor = pbr.roughnessFactor;
|
|
|
|
} catch (error) {
|
|
console.error(`Error saving material ${i}:`, error);
|
|
}
|
|
|
|
window_state.savedMaterials.push(materialState);
|
|
}
|
|
|
|
console.log('Materials state saved:', window_state.savedMaterials);
|
|
return true;
|
|
}
|
|
|
|
function hideTexture() {
|
|
console.log('hideTexture called');
|
|
|
|
if (!ensureModelLoaded()) {
|
|
console.error('Cannot hide texture: model not loaded');
|
|
return;
|
|
}
|
|
|
|
let appearanceButton = document.getElementById('appearance-button');
|
|
let geometryButton = document.getElementById('geometry-button');
|
|
appearanceButton.classList.remove('checked');
|
|
geometryButton.classList.add('checked');
|
|
|
|
// 如果已经隐藏,直接返回
|
|
if (window_state.isHidden) return;
|
|
|
|
// 第一次隐藏时保存状态
|
|
if (!window_state.savedMaterials) {
|
|
if (!saveMaterialsState()) return;
|
|
}
|
|
|
|
// 隐藏所有纹理
|
|
try {
|
|
for (let i = 0; i < modelViewer.model.materials.length; i++) {
|
|
const material = modelViewer.model.materials[i];
|
|
const pbr = material.pbrMetallicRoughness;
|
|
|
|
if (pbr.baseColorTexture) {
|
|
pbr.baseColorTexture.setTexture(null);
|
|
}
|
|
if (pbr.metallicRoughnessTexture) {
|
|
pbr.metallicRoughnessTexture.setTexture(null);
|
|
}
|
|
if (material.normalTexture) {
|
|
material.normalTexture.setTexture(null);
|
|
}
|
|
}
|
|
|
|
window_state.isHidden = true;
|
|
modelViewer.environmentImage = '/static/env_maps/gradient.jpg';
|
|
modelViewer.exposure = 4;
|
|
|
|
console.log('Textures hidden successfully');
|
|
} catch (error) {
|
|
console.error('Error hiding textures:', error);
|
|
}
|
|
}
|
|
|
|
function showTexture() {
|
|
console.log('showTexture called');
|
|
|
|
if (!ensureModelLoaded()) {
|
|
console.error('Cannot show texture: model not loaded');
|
|
return;
|
|
}
|
|
|
|
let appearanceButton = document.getElementById('appearance-button');
|
|
let geometryButton = document.getElementById('geometry-button');
|
|
appearanceButton.classList.add('checked');
|
|
geometryButton.classList.remove('checked');
|
|
|
|
// 如果不在隐藏状态,直接返回
|
|
if (!window_state.isHidden) return;
|
|
|
|
// 如果没有保存的材质状态,无法恢复
|
|
if (!window_state.savedMaterials) {
|
|
console.warn('No saved materials to restore');
|
|
return;
|
|
}
|
|
|
|
// 恢复纹理
|
|
try {
|
|
for (let i = 0; i < modelViewer.model.materials.length && i < window_state.savedMaterials.length; i++) {
|
|
const material = modelViewer.model.materials[i];
|
|
const pbr = material.pbrMetallicRoughness;
|
|
const savedMaterial = window_state.savedMaterials[i];
|
|
|
|
// 恢复纹理
|
|
if (savedMaterial.baseColorTexture && pbr.baseColorTexture) {
|
|
pbr.baseColorTexture.setTexture(savedMaterial.baseColorTexture);
|
|
console.log(`Restored baseColorTexture for material ${i}`);
|
|
}
|
|
if (savedMaterial.metallicRoughnessTexture && pbr.metallicRoughnessTexture) {
|
|
pbr.metallicRoughnessTexture.setTexture(savedMaterial.metallicRoughnessTexture);
|
|
console.log(`Restored metallicRoughnessTexture for material ${i}`);
|
|
}
|
|
if (savedMaterial.normalTexture && material.normalTexture) {
|
|
material.normalTexture.setTexture(savedMaterial.normalTexture);
|
|
console.log(`Restored normalTexture for material ${i}`);
|
|
}
|
|
|
|
// 恢复材质参数
|
|
if (savedMaterial.baseColorFactor) {
|
|
pbr.setBaseColorFactor(savedMaterial.baseColorFactor);
|
|
}
|
|
if (typeof savedMaterial.metallicFactor === 'number') {
|
|
pbr.setMetallicFactor(savedMaterial.metallicFactor);
|
|
}
|
|
if (typeof savedMaterial.roughnessFactor === 'number') {
|
|
pbr.setRoughnessFactor(savedMaterial.roughnessFactor);
|
|
}
|
|
}
|
|
|
|
// 恢复环境设置
|
|
modelViewer.environmentImage = '/static/env_maps/white.jpg';
|
|
if (window_state.savedExposure !== undefined) {
|
|
modelViewer.exposure = window_state.savedExposure;
|
|
}
|
|
|
|
window_state.isHidden = false;
|
|
console.log('Textures restored successfully');
|
|
|
|
} catch (error) {
|
|
console.error('Error restoring textures:', error);
|
|
}
|
|
}
|
|
|
|
// 添加调试函数
|
|
function debugMaterials() {
|
|
if (!ensureModelLoaded()) return;
|
|
|
|
console.log('=== Current Materials Debug ===');
|
|
modelViewer.model.materials.forEach((mat, idx) => {
|
|
const pbr = mat.pbrMetallicRoughness;
|
|
console.log(`Material ${idx}:`, {
|
|
name: mat.name,
|
|
baseColorTexture: pbr.baseColorTexture?.texture || null,
|
|
metallicRoughnessTexture: pbr.metallicRoughnessTexture?.texture || null,
|
|
normalTexture: mat.normalTexture?.texture || null,
|
|
baseColorFactor: pbr.baseColorFactor,
|
|
metallicFactor: pbr.metallicFactor,
|
|
roughnessFactor: pbr.roughnessFactor
|
|
});
|
|
});
|
|
console.log('Window state:', window_state);
|
|
console.log('===============================');
|
|
}
|
|
|
|
// 暴露调试函数到全局
|
|
window.debugMaterials = debugMaterials;
|
|
|
|
</script>
|
|
</body>
|
|
|
|
</html> |