feat(ui): add article TOC and interactive VRAM calculator for Wan 2.2
Deploy / build (push) Successful in 22s
Deploy / build (push) Successful in 22s
This commit is contained in:
1 parent
0dae8b0daa
commit
2c10cad52b
4 files changed
+609
-1
No files matched your search
@@ -0,0 +1,143 @@
|
||||
---
|
||||
import type { MarkdownHeading } from 'astro';
|
||||
|
||||
interface Props {
|
||||
headings: MarkdownHeading[];
|
||||
lang?: 'en' | 'zh';
|
||||
}
|
||||
|
||||
const { headings, lang = 'en' } = Astro.props;
|
||||
|
||||
// Filter for h2 and h3 headings only (level 2 & 3)
|
||||
const tocHeadings = headings.filter((h) => h.depth === 2 || h.depth === 3);
|
||||
const hasHeadings = tocHeadings.length >= 3;
|
||||
---
|
||||
|
||||
{hasHeadings && (
|
||||
<nav class="toc-container" aria-label={lang === 'zh' ? '文章目录' : 'Table of Contents'}>
|
||||
<details class="toc-details" open>
|
||||
<summary class="toc-summary">
|
||||
<span class="toc-title">
|
||||
<svg class="toc-icon" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<line x1="8" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="8" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="8" y1="18" x2="21" y2="18"></line>
|
||||
<line x1="3" y1="6" x2="3.01" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="3.01" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="3.01" y2="18"></line>
|
||||
</svg>
|
||||
{lang === 'zh' ? '目录' : 'Table of Contents'}
|
||||
</span>
|
||||
<span class="toc-count">({tocHeadings.length})</span>
|
||||
</summary>
|
||||
<ul class="toc-list">
|
||||
{tocHeadings.map((h) => (
|
||||
<li class={`toc-item depth-${h.depth}`}>
|
||||
<a href={`#${h.slug}`} class="toc-link">
|
||||
{h.text}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</details>
|
||||
</nav>
|
||||
)}
|
||||
|
||||
<style>
|
||||
.toc-container {
|
||||
margin: 2rem 0;
|
||||
padding: 0;
|
||||
border-radius: var(--radius, 8px);
|
||||
background: var(--surface, #16181d);
|
||||
border: 1px solid var(--border, #2d3139);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.toc-details {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.toc-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.85rem 1.15rem;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: var(--text, #e2e8f0);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
list-style: none;
|
||||
transition: background 0.15s ease;
|
||||
}
|
||||
|
||||
.toc-summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.toc-summary:hover {
|
||||
background: rgba(109, 141, 255, 0.06);
|
||||
}
|
||||
|
||||
.toc-title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.55rem;
|
||||
}
|
||||
|
||||
.toc-icon {
|
||||
color: var(--brand, #6d8dff);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.toc-count {
|
||||
font-size: 0.82rem;
|
||||
color: var(--muted, #94a3b8);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.toc-list {
|
||||
margin: 0;
|
||||
padding: 0.5rem 1.15rem 1rem 1.15rem;
|
||||
list-style: none;
|
||||
border-top: 1px solid var(--border, #2d3139);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.toc-item {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.toc-item.depth-3 {
|
||||
margin-left: 1.25rem;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.toc-link {
|
||||
color: var(--muted, #94a3b8);
|
||||
text-decoration: none;
|
||||
transition: color 0.15s ease, transform 0.15s ease;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.toc-link:hover {
|
||||
color: var(--brand, #6d8dff);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.toc-container {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
.toc-summary {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
.toc-list {
|
||||
padding: 0.5rem 1rem 0.85rem 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -61,6 +61,240 @@ Halving the pixel count does not halve the time — it cuts it to about a third.
|
||||
That is the usual superlinear scaling of a diffusion transformer, and it is the
|
||||
reason 480p exists as a draft tier at all.
|
||||
|
||||
<div class="interactive-calc" id="wan-vram-calc">
|
||||
<div class="calc-header">
|
||||
<span class="calc-badge">Interactive Tool</span>
|
||||
<h4>Wan 2.2 TI2V-5B Render Time & VRAM Estimator</h4>
|
||||
<p>Simulate sampling duration, decode memory, and safe upscale segmentation based on my RTX 3060 12 GB benchmarks.</p>
|
||||
</div>
|
||||
<div class="calc-grid">
|
||||
<div class="calc-field">
|
||||
<label for="calc-res">Resolution Tier</label>
|
||||
<select id="calc-res">
|
||||
<option value="480">832×480 (Fast Draft — ~6 min / 5s)</option>
|
||||
<option value="704" selected>1280×704 (Production Base — ~18 min / 5s)</option>
|
||||
<option value="736">1280×736 (Multiple of 32 for true 1080p — ~19.5 min / 5s)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="calc-field">
|
||||
<label for="calc-sec">Clip Duration (seconds)</label>
|
||||
<div class="calc-slider-wrap">
|
||||
<input type="range" id="calc-sec" min="1" max="10" step="1" value="5">
|
||||
<span class="calc-val" id="calc-sec-val">5 s (121 frames @ 24fps)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="calc-field">
|
||||
<label for="calc-steps">Sampling Steps</label>
|
||||
<div class="calc-slider-wrap">
|
||||
<input type="range" id="calc-steps" min="10" max="30" step="5" value="20">
|
||||
<span class="calc-val" id="calc-steps-val">20 steps</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="calc-results">
|
||||
<div class="calc-stat">
|
||||
<span class="stat-label">Estimated Total Render</span>
|
||||
<span class="stat-num" id="stat-time">~17.9 min</span>
|
||||
<span class="stat-sub" id="stat-breakdown">15.1m sampling + 2.8m decode</span>
|
||||
</div>
|
||||
<div class="calc-stat">
|
||||
<span class="stat-label">Peak VRAM Usage</span>
|
||||
<span class="stat-num" id="stat-vram">12.1 GB</span>
|
||||
<span class="stat-sub" id="stat-vram-note">Tight fit on 12 GB card</span>
|
||||
</div>
|
||||
<div class="calc-stat">
|
||||
<span class="stat-label">SeedVR2 1.5× Split Strategy</span>
|
||||
<span class="stat-num" id="stat-split">3 segments</span>
|
||||
<span class="stat-sub" id="stat-split-note">Max 41 frames/segment to prevent crash</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.interactive-calc {
|
||||
margin: 2.2rem 0;
|
||||
padding: 1.4rem;
|
||||
border-radius: var(--radius, 8px);
|
||||
background: var(--surface, #16181d);
|
||||
border: 1px solid var(--border, #2d3139);
|
||||
}
|
||||
.calc-header {
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
.calc-badge {
|
||||
display: inline-block;
|
||||
padding: 0.15rem 0.5rem;
|
||||
font-size: 0.72rem;
|
||||
font-family: var(--font-mono);
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
color: var(--brand, #6d8dff);
|
||||
background: rgba(109, 141, 255, 0.12);
|
||||
border-radius: 4px;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.calc-header h4 {
|
||||
margin: 0.2rem 0 0.4rem;
|
||||
font-size: 1.15rem;
|
||||
color: var(--text, #e2e8f0);
|
||||
}
|
||||
.calc-header p {
|
||||
margin: 0;
|
||||
font-size: 0.88rem;
|
||||
color: var(--muted, #94a3b8);
|
||||
}
|
||||
.calc-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 1.1rem;
|
||||
margin-bottom: 1.4rem;
|
||||
}
|
||||
.calc-field label {
|
||||
display: block;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
color: var(--muted, #94a3b8);
|
||||
margin-bottom: 0.45rem;
|
||||
}
|
||||
.calc-field select {
|
||||
width: 100%;
|
||||
padding: 0.55rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
background: var(--bg, #0b0c0e);
|
||||
border: 1px solid var(--border, #2d3139);
|
||||
color: var(--text, #e2e8f0);
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
.calc-slider-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
.calc-slider-wrap input[type="range"] {
|
||||
width: 100%;
|
||||
accent-color: var(--brand, #6d8dff);
|
||||
cursor: pointer;
|
||||
}
|
||||
.calc-val {
|
||||
font-size: 0.82rem;
|
||||
font-family: var(--font-mono);
|
||||
color: var(--brand, #6d8dff);
|
||||
}
|
||||
.calc-results {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 1rem;
|
||||
padding-top: 1.2rem;
|
||||
border-top: 1px solid var(--border, #2d3139);
|
||||
}
|
||||
.calc-stat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.stat-label {
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted, #94a3b8);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.stat-num {
|
||||
font-size: 1.35rem;
|
||||
font-weight: 700;
|
||||
font-family: var(--font-mono);
|
||||
color: var(--text, #e2e8f0);
|
||||
}
|
||||
.stat-sub {
|
||||
font-size: 0.78rem;
|
||||
color: var(--muted, #94a3b8);
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.interactive-calc { padding: 1rem; }
|
||||
.calc-results { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
(function initCalc() {
|
||||
const resEl = document.getElementById('calc-res');
|
||||
const secEl = document.getElementById('calc-sec');
|
||||
const secVal = document.getElementById('calc-sec-val');
|
||||
const stepsEl = document.getElementById('calc-steps');
|
||||
const stepsVal = document.getElementById('calc-steps-val');
|
||||
const timeEl = document.getElementById('stat-time');
|
||||
const bdEl = document.getElementById('stat-breakdown');
|
||||
const vramEl = document.getElementById('stat-vram');
|
||||
const vramNote = document.getElementById('stat-vram-note');
|
||||
const splitEl = document.getElementById('stat-split');
|
||||
const splitNote = document.getElementById('stat-split-note');
|
||||
|
||||
if (!resEl || !secEl || !stepsEl) return;
|
||||
|
||||
function update() {
|
||||
const res = resEl.value;
|
||||
const sec = parseInt(secEl.value, 10);
|
||||
const steps = parseInt(stepsEl.value, 10);
|
||||
|
||||
// 1s = 25 frames, 5s = 121 frames
|
||||
const frames = sec === 1 ? 25 : (sec - 1) * 24 + 25;
|
||||
secVal.textContent = `${sec} s (${frames} frames @ 24fps)`;
|
||||
stepsVal.textContent = `${steps} steps`;
|
||||
|
||||
// Measured constants for 5s (121 frames, 20 steps):
|
||||
// 480p: sample 4.8m, decode 1.3m
|
||||
// 704p: sample 15.1m, decode 2.8m
|
||||
// 736p: ~16.5m, decode 3.0m
|
||||
let baseSamplePerFrameStep = 0;
|
||||
let baseDecodePerFrame = 0;
|
||||
let vramPeak = 0;
|
||||
|
||||
if (res === '480') {
|
||||
baseSamplePerFrameStep = 4.8 / (121 * 20);
|
||||
baseDecodePerFrame = 1.3 / 121;
|
||||
vramPeak = 10.3;
|
||||
} else if (res === '704') {
|
||||
baseSamplePerFrameStep = 15.1 / (121 * 20);
|
||||
baseDecodePerFrame = 2.8 / 121;
|
||||
vramPeak = 12.1;
|
||||
} else {
|
||||
baseSamplePerFrameStep = 16.5 / (121 * 20);
|
||||
baseDecodePerFrame = 3.0 / 121;
|
||||
vramPeak = 12.2;
|
||||
}
|
||||
|
||||
const sampleTime = frames * steps * baseSamplePerFrameStep;
|
||||
const decodeTime = frames * baseDecodePerFrame;
|
||||
const totalTime = sampleTime + decodeTime;
|
||||
|
||||
timeEl.textContent = `~${totalTime.toFixed(1)} min`;
|
||||
bdEl.textContent = `${sampleTime.toFixed(1)}m sampling + ${decodeTime.toFixed(1)}m decode`;
|
||||
|
||||
vramEl.textContent = `${vramPeak} GB`;
|
||||
if (vramPeak > 12.0) {
|
||||
vramNote.textContent = 'Requires tiled VAE decode to survive 12 GB';
|
||||
vramNote.style.color = '#f59e0b';
|
||||
} else {
|
||||
vramNote.textContent = 'Fits in 12 GB with clean cache';
|
||||
vramNote.style.color = 'var(--muted)';
|
||||
}
|
||||
|
||||
// SeedVR2 segmenting (limit 41 frames)
|
||||
const segments = Math.ceil(frames / 41);
|
||||
splitEl.textContent = `${segments} segment${segments > 1 ? 's' : ''}`;
|
||||
if (frames > 41) {
|
||||
splitNote.textContent = `Cut into ≤41 frames (prevents access violation)`;
|
||||
} else {
|
||||
splitNote.textContent = `Single pass is safe (≤41 frames)`;
|
||||
}
|
||||
}
|
||||
|
||||
resEl.addEventListener('change', update);
|
||||
secEl.addEventListener('input', update);
|
||||
stepsEl.addEventListener('input', update);
|
||||
update();
|
||||
})();
|
||||
</script>
|
||||
|
||||
## Failure one: the decode that runs out of host memory
|
||||
|
||||
The first 720p attempt sampled fine and then died in the VAE decode:
|
||||
|
||||
@@ -43,6 +43,234 @@ banner: /banners/running-ai-video-locally-on-a-12gb-gpu.jpg
|
||||
|
||||
像素数减半,时间不是减半,而是降到约三分之一。这是扩散 Transformer 典型的超线性关系,也正是 480p 这档草稿存在的理由。
|
||||
|
||||
<div class="interactive-calc" id="wan-vram-calc">
|
||||
<div class="calc-header">
|
||||
<span class="calc-badge">交互工具</span>
|
||||
<h4>Wan 2.2 TI2V-5B 渲染时间与显存估算器</h4>
|
||||
<p>基于 RTX 3060 12GB 实测基准数据,实时模拟不同分辨率、秒数与步数下的采样时长、显存需求与分段策略。</p>
|
||||
</div>
|
||||
<div class="calc-grid">
|
||||
<div class="calc-field">
|
||||
<label for="calc-res">生成分辨率档位</label>
|
||||
<select id="calc-res">
|
||||
<option value="480">832×480(快速草稿档 — 约 6 分钟 / 5秒)</option>
|
||||
<option value="704" selected>1280×704(生产基础档 — 约 18 分钟 / 5秒)</option>
|
||||
<option value="736">1280×736(32整倍数,真1080p — 约 19.5 分钟 / 5秒)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="calc-field">
|
||||
<label for="calc-sec">镜头时长(秒)</label>
|
||||
<div class="calc-slider-wrap">
|
||||
<input type="range" id="calc-sec" min="1" max="10" step="1" value="5">
|
||||
<span class="calc-val" id="calc-sec-val">5 秒(121 帧 @ 24fps)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="calc-field">
|
||||
<label for="calc-steps">采样步数(Steps)</label>
|
||||
<div class="calc-slider-wrap">
|
||||
<input type="range" id="calc-steps" min="10" max="30" step="5" value="20">
|
||||
<span class="calc-val" id="calc-steps-val">20 步</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="calc-results">
|
||||
<div class="calc-stat">
|
||||
<span class="stat-label">预估总渲染耗时</span>
|
||||
<span class="stat-num" id="stat-time">~17.9 分钟</span>
|
||||
<span class="stat-sub" id="stat-breakdown">15.1分 采样 + 2.8分 解码</span>
|
||||
</div>
|
||||
<div class="calc-stat">
|
||||
<span class="stat-label">峰值显存占用</span>
|
||||
<span class="stat-num" id="stat-vram">12.1 GB</span>
|
||||
<span class="stat-sub" id="stat-vram-note">12GB 显卡极限贴线</span>
|
||||
</div>
|
||||
<div class="calc-stat">
|
||||
<span class="stat-label">SeedVR2 1.5× 放大分段策略</span>
|
||||
<span class="stat-num" id="stat-split">3 个切片</span>
|
||||
<span class="stat-sub" id="stat-split-note">每段 ≤41 帧,防止原生崩溃</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.interactive-calc {
|
||||
margin: 2.2rem 0;
|
||||
padding: 1.4rem;
|
||||
border-radius: var(--radius, 8px);
|
||||
background: var(--surface, #16181d);
|
||||
border: 1px solid var(--border, #2d3139);
|
||||
}
|
||||
.calc-header {
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
.calc-badge {
|
||||
display: inline-block;
|
||||
padding: 0.15rem 0.5rem;
|
||||
font-size: 0.72rem;
|
||||
font-family: var(--font-mono);
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
color: var(--brand, #6d8dff);
|
||||
background: rgba(109, 141, 255, 0.12);
|
||||
border-radius: 4px;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.calc-header h4 {
|
||||
margin: 0.2rem 0 0.4rem;
|
||||
font-size: 1.15rem;
|
||||
color: var(--text, #e2e8f0);
|
||||
}
|
||||
.calc-header p {
|
||||
margin: 0;
|
||||
font-size: 0.88rem;
|
||||
color: var(--muted, #94a3b8);
|
||||
}
|
||||
.calc-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 1.1rem;
|
||||
margin-bottom: 1.4rem;
|
||||
}
|
||||
.calc-field label {
|
||||
display: block;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
color: var(--muted, #94a3b8);
|
||||
margin-bottom: 0.45rem;
|
||||
}
|
||||
.calc-field select {
|
||||
width: 100%;
|
||||
padding: 0.55rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
background: var(--bg, #0b0c0e);
|
||||
border: 1px solid var(--border, #2d3139);
|
||||
color: var(--text, #e2e8f0);
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
.calc-slider-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
.calc-slider-wrap input[type="range"] {
|
||||
width: 100%;
|
||||
accent-color: var(--brand, #6d8dff);
|
||||
cursor: pointer;
|
||||
}
|
||||
.calc-val {
|
||||
font-size: 0.82rem;
|
||||
font-family: var(--font-mono);
|
||||
color: var(--brand, #6d8dff);
|
||||
}
|
||||
.calc-results {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 1rem;
|
||||
padding-top: 1.2rem;
|
||||
border-top: 1px solid var(--border, #2d3139);
|
||||
}
|
||||
.calc-stat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.stat-label {
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted, #94a3b8);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.stat-num {
|
||||
font-size: 1.35rem;
|
||||
font-weight: 700;
|
||||
font-family: var(--font-mono);
|
||||
color: var(--text, #e2e8f0);
|
||||
}
|
||||
.stat-sub {
|
||||
font-size: 0.78rem;
|
||||
color: var(--muted, #94a3b8);
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.interactive-calc { padding: 1rem; }
|
||||
.calc-results { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
(function initCalc() {
|
||||
const resEl = document.getElementById('calc-res');
|
||||
const secEl = document.getElementById('calc-sec');
|
||||
const secVal = document.getElementById('calc-sec-val');
|
||||
const stepsEl = document.getElementById('calc-steps');
|
||||
const stepsVal = document.getElementById('calc-steps-val');
|
||||
const timeEl = document.getElementById('stat-time');
|
||||
const bdEl = document.getElementById('stat-breakdown');
|
||||
const vramEl = document.getElementById('stat-vram');
|
||||
const vramNote = document.getElementById('stat-vram-note');
|
||||
const splitEl = document.getElementById('stat-split');
|
||||
const splitNote = document.getElementById('stat-split-note');
|
||||
|
||||
if (!resEl || !secEl || !stepsEl) return;
|
||||
|
||||
function update() {
|
||||
const res = resEl.value;
|
||||
const sec = parseInt(secEl.value, 10);
|
||||
const steps = parseInt(stepsEl.value, 10);
|
||||
|
||||
const frames = sec === 1 ? 25 : (sec - 1) * 24 + 25;
|
||||
secVal.textContent = `${sec} 秒(${frames} 帧 @ 24fps)`;
|
||||
stepsVal.textContent = `${steps} 步`;
|
||||
|
||||
let baseSamplePerFrameStep = 0;
|
||||
let baseDecodePerFrame = 0;
|
||||
let vramPeak = 0;
|
||||
|
||||
if (res === '480') {
|
||||
baseSamplePerFrameStep = 4.8 / (121 * 20);
|
||||
baseDecodePerFrame = 1.3 / 121;
|
||||
vramPeak = 10.3;
|
||||
} else if (res === '704') {
|
||||
baseSamplePerFrameStep = 15.1 / (121 * 20);
|
||||
baseDecodePerFrame = 2.8 / 121;
|
||||
vramPeak = 12.1;
|
||||
} else {
|
||||
baseSamplePerFrameStep = 16.5 / (121 * 20);
|
||||
baseDecodePerFrame = 3.0 / 121;
|
||||
vramPeak = 12.2;
|
||||
}
|
||||
|
||||
const sampleTime = frames * steps * baseSamplePerFrameStep;
|
||||
const decodeTime = frames * baseDecodePerFrame;
|
||||
const totalTime = sampleTime + decodeTime;
|
||||
|
||||
timeEl.textContent = `~${totalTime.toFixed(1)} 分钟`;
|
||||
bdEl.textContent = `${sampleTime.toFixed(1)}分 采样 + ${decodeTime.toFixed(1)}分 解码`;
|
||||
|
||||
vramEl.textContent = `${vramPeak} GB`;
|
||||
if (vramPeak > 12.0) {
|
||||
vramNote.textContent = '需要开启分块 VAE 解码才能跑通 12GB';
|
||||
vramNote.style.color = '#f59e0b';
|
||||
} else {
|
||||
vramNote.textContent = '缓存清理后可直接装进 12GB';
|
||||
vramNote.style.color = 'var(--muted)';
|
||||
}
|
||||
|
||||
const segments = Math.ceil(frames / 41);
|
||||
splitEl.textContent = `${segments} 个切片`;
|
||||
if (frames > 41) {
|
||||
splitNote.textContent = '必须切成 ≤41 帧(防止原生异常崩溃)';
|
||||
} else {
|
||||
splitNote.textContent = '单次直出安全(≤41 帧)';
|
||||
}
|
||||
}
|
||||
|
||||
resEl.addEventListener('change', update);
|
||||
secEl.addEventListener('input', update);
|
||||
stepsEl.addEventListener('input', update);
|
||||
update();
|
||||
})();
|
||||
</script>
|
||||
|
||||
## 第一次失败:解码把主机内存吃光
|
||||
|
||||
第一次 720p 采样很顺利,然后死在 VAE 解码:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import AuthorCard from '../../components/AuthorCard.astro';
|
||||
import TableOfContents from '../../components/TableOfContents.astro';
|
||||
import { getCollection, render } from 'astro:content';
|
||||
import { postLang } from '../../lib/lang';
|
||||
import { countWords, readingMinutes } from '../../lib/reading-time';
|
||||
@@ -16,7 +17,7 @@ export async function getStaticPaths() {
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
const { Content } = await render(post);
|
||||
const { Content, headings } = await render(post);
|
||||
|
||||
const lang = postLang(post.slug);
|
||||
|
||||
@@ -119,6 +120,8 @@ const jsonLd = {
|
||||
</figure>
|
||||
)}
|
||||
|
||||
<TableOfContents headings={headings} lang={lang} />
|
||||
|
||||
<Content />
|
||||
|
||||
<AuthorCard />
|
||||
|
||||
Reference in new issue
Block a user