From 8f5712629f1950efd8ab9ecbd2a1efc2f828bfc2 Mon Sep 17 00:00:00 2001 From: Mihir Jagtap <94254893+MihirJagtap@users.noreply.github.com> Date: Tue, 4 Nov 2025 14:31:56 -0800 Subject: [PATCH] [docs] port to mkdocs (#855) --- .github/workflows/docs.yml | 63 ++-- .gitignore | 17 +- csrc/attn/sliding_tile_attn/README.md | 2 +- docs/Makefile | 26 -- docs/README.md | 39 ++- docs/api/fastvideo.md | 248 ++++++++++++++++ docs/api/summary.md | 27 ++ docs/assets/custom.css | 41 +++ docs/{source/_static => assets}/custom.js | 0 docs/assets/images/STA_configuration.png | Bin 0 -> 100130 bytes .../_static => assets}/images/fastwan.png | Bin .../_static => assets}/images/perf.png | Bin .../_static => assets}/images/runpod_cuda.png | Bin .../_static => assets}/images/runpod_ssh.png | Bin .../images/runpod_template.png | Bin docs/assets/logos/icon_simple.svg | 6 + docs/assets/logos/logo.svg | 18 ++ .../contributing/developer_env/docker.md | 2 +- .../contributing/developer_env/index.md | 8 - .../contributing/developer_env/runpod.md | 7 +- docs/{source => }/contributing/overview.md | 1 - docs/{source => }/contributing/profiling.md | 0 docs/{source => }/design/overview.md | 15 - .../distillation/data_preprocess.md | 1 - docs/{source => }/distillation/dmd.md | 0 docs/examples/examples_index.md | 12 + docs/{source => }/generate_examples.py | 148 +++++++--- docs/getting_started/installation.md | 41 +++ .../getting_started/installation/gpu.md | 9 - .../getting_started/installation/mps.md | 9 - docs/getting_started/quick_start.md | 55 ++++ docs/getting_started/v1_api.md | 42 +++ docs/{source => }/index.md | 121 ++------ docs/{source => }/inference/add_pipeline.md | 1 - docs/{source => }/inference/cli.md | 0 docs/{source => }/inference/comfyui.md | 0 docs/{source => }/inference/configuration.md | 2 +- .../inference/inference_quick_start.md | 0 docs/{source => }/inference/optimizations.md | 19 -- docs/inference/support_matrix.md | 66 +++++ docs/make.bat | 35 --- docs/requirements-docs.txt | 15 - .../sliding_tile_attention/demo.md | 1 - .../sliding_tile_attention/installation.md | 1 - docs/source/_static/custom.css | 8 - docs/source/_templates/sections/header.html | 39 --- docs/source/api/summary.md | 19 -- docs/source/autodoc2_docstring_parser.py | 22 -- docs/source/conf.py | 275 ------------------ docs/source/getting_started/installation.md | 18 -- docs/source/getting_started/v1_api.md | 83 ------ docs/source/inference/support_matrix.md | 136 --------- docs/{source => }/training/data_preprocess.md | 1 - docs/{source => }/training/finetune.md | 2 +- .../video_sparse_attention/installation.md | 1 - mkdocs.yml | 169 +++++++++++ requirements-mkdocs.txt | 13 + 57 files changed, 936 insertions(+), 948 deletions(-) delete mode 100644 docs/Makefile create mode 100644 docs/api/fastvideo.md create mode 100644 docs/api/summary.md create mode 100644 docs/assets/custom.css rename docs/{source/_static => assets}/custom.js (100%) create mode 100644 docs/assets/images/STA_configuration.png rename docs/{source/_static => assets}/images/fastwan.png (100%) rename docs/{source/_static => assets}/images/perf.png (100%) rename docs/{source/_static => assets}/images/runpod_cuda.png (100%) rename docs/{source/_static => assets}/images/runpod_ssh.png (100%) rename docs/{source/_static => assets}/images/runpod_template.png (100%) create mode 100644 docs/assets/logos/icon_simple.svg create mode 100644 docs/assets/logos/logo.svg rename docs/{source => }/contributing/developer_env/docker.md (98%) rename docs/{source => }/contributing/developer_env/index.md (75%) rename docs/{source => }/contributing/developer_env/runpod.md (86%) rename docs/{source => }/contributing/overview.md (98%) rename docs/{source => }/contributing/profiling.md (100%) rename docs/{source => }/design/overview.md (97%) rename docs/{source => }/distillation/data_preprocess.md (98%) rename docs/{source => }/distillation/dmd.md (100%) create mode 100644 docs/examples/examples_index.md rename docs/{source => }/generate_examples.py (76%) create mode 100644 docs/getting_started/installation.md rename docs/{source => }/getting_started/installation/gpu.md (86%) rename docs/{source => }/getting_started/installation/mps.md (84%) create mode 100644 docs/getting_started/quick_start.md create mode 100644 docs/getting_started/v1_api.md rename docs/{source => }/index.md (54%) rename docs/{source => }/inference/add_pipeline.md (99%) rename docs/{source => }/inference/cli.md (100%) rename docs/{source => }/inference/comfyui.md (100%) rename docs/{source => }/inference/configuration.md (98%) rename docs/{source => }/inference/inference_quick_start.md (100%) rename docs/{source => }/inference/optimizations.md (94%) create mode 100644 docs/inference/support_matrix.md delete mode 100644 docs/make.bat delete mode 100644 docs/requirements-docs.txt rename docs/{source => }/sliding_tile_attention/demo.md (97%) rename docs/{source => }/sliding_tile_attention/installation.md (98%) delete mode 100644 docs/source/_static/custom.css delete mode 100644 docs/source/_templates/sections/header.html delete mode 100644 docs/source/api/summary.md delete mode 100644 docs/source/autodoc2_docstring_parser.py delete mode 100644 docs/source/conf.py delete mode 100644 docs/source/getting_started/installation.md delete mode 100644 docs/source/getting_started/v1_api.md delete mode 100644 docs/source/inference/support_matrix.md rename docs/{source => }/training/data_preprocess.md (98%) rename docs/{source => }/training/finetune.md (99%) rename docs/{source => }/video_sparse_attention/installation.md (98%) create mode 100644 mkdocs.yml create mode 100644 requirements-mkdocs.txt diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1a4f4eb4a..4be7396c6 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,82 +1,55 @@ -# Sample workflow for building and deploying a Hugo site to GitHub Pages -name: Deploy FastVideo Docs to Pages +name: Deploy Documentation on: - # Runs on pushes targeting the default branch push: - branches: - - main - paths: - - "docs/**/*.md" - - "fastvideo/examples/**/*.py" + branches: [ main ] pull_request: - branches: - - main - types: [opened, ready_for_review, synchronize, reopened] - paths: - - "docs/**/*.md" - - "fastvideo/examples/**/*.py" + branches: [ main ] - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read pages: write id-token: write -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: group: "pages" cancel-in-progress: false -# Default to bash -defaults: - run: - shell: bash - jobs: - pre-commit: - uses: ./.github/workflows/pre-commit.yml - - # Build job build: runs-on: ubuntu-latest - needs: pre-commit steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup Pages - id: pages - uses: actions/configure-pages@v5 - - name: Set up Python + + - name: Setup Python uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: '3.12' + - name: Install dependencies run: | - cd docs - pip install -r requirements-docs.txt - - name: Build docs - run: | - cd docs - make clean - make html + python -m pip install --upgrade pip + pip install -r requirements-mkdocs.txt + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Build documentation + run: mkdocs build + - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: ./docs/build/html + path: ./site - # Deployment job deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} - if: ${{ github.event_name == 'push' }} runs-on: ubuntu-latest needs: build + if: github.ref == 'refs/heads/main' steps: - name: Deploy to GitHub Pages id: deployment diff --git a/.gitignore b/.gitignore index bb17640a5..c4023db07 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ wandb/ *.pt cache_dir/ wandb/ +venv/ +.venv/ runs/ samples/ *validation/ @@ -37,12 +39,13 @@ dist/ eggs/ .eggs/ -# Sphinx documentation -docs/_build/ -docs/source/getting_started/examples/ -docs/source/inference/examples/ -docs/source/training/examples/ -docs/source/distillation/examples/ +# MkDocs documentation +site/ +docs/getting_started/examples/ +docs/inference/examples/ +docs/training/examples/ +docs/distillation/examples/ +!requirements-mkdocs.txt # VSCode .vscode/ @@ -61,7 +64,7 @@ docs/source/distillation/examples/ !fastvideo/tests/ssim/reference_videos/**/*.mp4 # Static images -!docs/source/_static/images/**/*.png +!docs/assets/images/**/*.png !comfyui/assets/**/*.png !comfyui/assets/**/*.gif diff --git a/csrc/attn/sliding_tile_attn/README.md b/csrc/attn/sliding_tile_attn/README.md index 38c708115..4c6bfcb47 100644 --- a/csrc/attn/sliding_tile_attn/README.md +++ b/csrc/attn/sliding_tile_attn/README.md @@ -77,7 +77,7 @@ https://github.com/user-attachments/assets/f3b6dd79-7b43-4b60-a0fa-3d6495ec5747 Here is a diagram of how the window is configured and passed through the FastVideo pipeline:
- +
diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 3745dc403..000000000 --- a/docs/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -clean: - @$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - rm -rf "$(SOURCEDIR)/getting_started/examples" - rm -rf "$(SOURCEDIR)/inference/examples" - rm -rf "$(SOURCEDIR)/training/examples" diff --git a/docs/README.md b/docs/README.md index 0a3151ad7..0bcab3afc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,20 +1,39 @@ -# FastVideo documents +# FastVideo Documentation -## Build the docs +This directory contains the FastVideo documentation built with MkDocs. + +## Build the docs locally ```bash -# Install dependencies. -pip install -r requirements-docs.txt +# Install dependencies +pip install -r docs/requirements-mkdocs.txt -# Build the docs. -make clean -make html +# Serve docs with live reload (recommended for development) +mkdocs serve + +# Or build static site +mkdocs build ``` -## Open the docs with your browser +## View the docs + +### Development server (with live reload) ```bash -python -m http.server -d build/html/ +mkdocs serve ``` -Launch your browser and open localhost:8000. +Then open your browser to: http://127.0.0.1:8000 + +### Static build + +```bash +mkdocs build +python -m http.server -d site/ +``` + +Then open your browser to: http://localhost:8000 + +## Automatic Deployment + +Documentation is automatically built and deployed to GitHub Pages when changes are pushed to the `main` branch via the `.github/workflows/docs.yml` workflow. diff --git a/docs/api/fastvideo.md b/docs/api/fastvideo.md new file mode 100644 index 000000000..2f81a3995 --- /dev/null +++ b/docs/api/fastvideo.md @@ -0,0 +1,248 @@ +# FastVideo API Reference + +This page contains the complete API reference for the FastVideo library. + +## fastvideo + +### Modules + +| Name | Description | +|------|-------------| +| [attention](#fastvideoattention) | Attention mechanisms and backends for video generation | +| [configs](#fastvideoconfigs) | Configuration classes for pipelines, models, and sampling | +| [distributed](#fastvideodistributed) | Distributed execution and communication utilities | +| [entrypoints](#fastvideoentrypoints) | Main API entry points for video generation | +| [models](#fastvideomodels) | Model implementations (transformers, VAEs, schedulers) | +| [pipelines](#fastvideopipelines) | Core pipeline classes for video diffusion | +| [training](#fastvideotraining) | Training utilities and helpers | +| [workflow](#fastvideoworkflow) | Workflow management and orchestration | +| [dataset](#fastvideodataset) | Dataset handling and preprocessing | +| [layers](#fastvideolayers) | Custom neural network layers | +| [platforms](#fastvideoplatforms) | Platform-specific implementations | +| [utils](#fastvideoutils) | Utility functions and helpers | +| [worker](#fastvideoworker) | Execution workers for video generation | + +## fastvideo.attention + +::: fastvideo.attention + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_submodules: true + heading_level: 3 + +## fastvideo.configs + +::: fastvideo.configs + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_submodules: true + heading_level: 3 + +### Submodules + +#### fastvideo.configs.pipelines + +::: fastvideo.configs.pipelines + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + heading_level: 4 + +#### fastvideo.configs.models + +::: fastvideo.configs.models + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + heading_level: 4 + +#### fastvideo.configs.sample + +::: fastvideo.configs.sample + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + heading_level: 4 + +## fastvideo.distributed + +::: fastvideo.distributed + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_submodules: true + heading_level: 3 + +## fastvideo.entrypoints + +::: fastvideo.entrypoints + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_submodules: true + heading_level: 3 + +## fastvideo.models + +::: fastvideo.models + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_submodules: true + heading_level: 3 + +### Submodules + +#### fastvideo.models.registry + +::: fastvideo.models.registry + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + heading_level: 4 + +#### fastvideo.models.loader + +::: fastvideo.models.loader + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + heading_level: 4 + +## fastvideo.pipelines + +::: fastvideo.pipelines + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_submodules: true + heading_level: 3 + +### Submodules + +#### fastvideo.pipelines.composed_pipeline_base + +::: fastvideo.pipelines.composed_pipeline_base + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + heading_level: 4 + +#### fastvideo.pipelines.lora_pipeline + +::: fastvideo.pipelines.lora_pipeline + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + heading_level: 4 + +#### fastvideo.pipelines.pipeline_batch_info + +::: fastvideo.pipelines.pipeline_batch_info + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + heading_level: 4 + +#### fastvideo.pipelines.pipeline_registry + +::: fastvideo.pipelines.pipeline_registry + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + heading_level: 4 + +#### fastvideo.pipelines.stages + +::: fastvideo.pipelines.stages + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + heading_level: 4 + +## fastvideo.training + +::: fastvideo.training + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_submodules: true + heading_level: 3 + +## fastvideo.workflow + +::: fastvideo.workflow + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_submodules: true + heading_level: 3 + +## fastvideo.dataset + +::: fastvideo.dataset + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_submodules: true + heading_level: 3 + +## fastvideo.layers + +::: fastvideo.layers + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_submodules: true + heading_level: 3 + +## fastvideo.platforms + +::: fastvideo.platforms + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_submodules: true + heading_level: 3 + +## fastvideo.utils + +::: fastvideo.utils + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + heading_level: 3 + +## fastvideo.worker + +::: fastvideo.worker + options: + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_submodules: true + heading_level: 3 diff --git a/docs/api/summary.md b/docs/api/summary.md new file mode 100644 index 000000000..76d751a63 --- /dev/null +++ b/docs/api/summary.md @@ -0,0 +1,27 @@ +# API Summary + +This page provides a quick overview of the main FastVideo API components. + +## Video Generator + +::: fastvideo.VideoGenerator + options: + show_root_heading: false + show_source: false + heading_level: 3 + +## Initialization Configuration + +::: fastvideo.PipelineConfig + options: + show_root_heading: false + show_source: false + heading_level: 3 + +## Sampling Configuration + +::: fastvideo.SamplingParam + options: + show_root_heading: false + show_source: false + heading_level: 3 diff --git a/docs/assets/custom.css b/docs/assets/custom.css new file mode 100644 index 000000000..7bf389a10 --- /dev/null +++ b/docs/assets/custom.css @@ -0,0 +1,41 @@ +.vertical-table-header th.head:not(.stub) { + writing-mode: sideways-lr; + white-space: nowrap; + max-width: 0; + p { + margin: 0; + } +} + +/* Image sizing classes */ +.image-small { + max-width: 200px; + height: auto; +} + +.image-medium { + max-width: 400px; + height: auto; +} + +.image-large { + max-width: 600px; + height: auto; +} + +.image-full { + max-width: 100%; + height: auto; +} + +/* Responsive images */ +img { + max-width: 100%; + height: auto; +} + +/* Center images */ +.image-center { + display: block; + margin: 0 auto; +} diff --git a/docs/source/_static/custom.js b/docs/assets/custom.js similarity index 100% rename from docs/source/_static/custom.js rename to docs/assets/custom.js diff --git a/docs/assets/images/STA_configuration.png b/docs/assets/images/STA_configuration.png new file mode 100644 index 0000000000000000000000000000000000000000..7b96e9b532d6f347ca9b38b89f37aa701c66dd09 GIT binary patch literal 100130 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
5=3l|W6(-02H_0%0nJNd%BbdZr2>2D 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

H!#6*|rA;Lyl3xAz|D* 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 l`u=??ZF4sMAx?yFQ6W0nIbEns(ip!Ha + + + + + diff --git a/docs/assets/logos/logo.svg b/docs/assets/logos/logo.svg new file mode 100644 index 000000000..0b8b058b5 --- /dev/null +++ b/docs/assets/logos/logo.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/docs/source/contributing/developer_env/docker.md b/docs/contributing/developer_env/docker.md similarity index 98% rename from docs/source/contributing/developer_env/docker.md rename to docs/contributing/developer_env/docker.md index c8f411261..0de910e81 100644 --- a/docs/source/contributing/developer_env/docker.md +++ b/docs/contributing/developer_env/docker.md @@ -1,4 +1,4 @@ -(docker)= + # 🐳 Using the FastVideo Docker Image If you prefer a containerized development environment or want to avoid managing dependencies manually, you can use our prebuilt Docker image: diff --git a/docs/source/contributing/developer_env/index.md b/docs/contributing/developer_env/index.md similarity index 75% rename from docs/source/contributing/developer_env/index.md rename to docs/contributing/developer_env/index.md index 56a63a877..a093eda94 100644 --- a/docs/source/contributing/developer_env/index.md +++ b/docs/contributing/developer_env/index.md @@ -3,11 +3,3 @@ # 🧰 Developer Environment Accelerate your FastVideo development workflow by leveraging Docker images and cloud GPUs for efficient experimentation and reproducible environments. - -:::{toctree} -:caption: Contents -:maxdepth: 1 - -docker -runpod -::: diff --git a/docs/source/contributing/developer_env/runpod.md b/docs/contributing/developer_env/runpod.md similarity index 86% rename from docs/source/contributing/developer_env/runpod.md rename to docs/contributing/developer_env/runpod.md index 8ebe07015..0e6cb11ca 100644 --- a/docs/source/contributing/developer_env/runpod.md +++ b/docs/contributing/developer_env/runpod.md @@ -1,4 +1,3 @@ -(runpod)= # 📦 Developing FastVideo on RunPod @@ -10,7 +9,7 @@ Choose a GPU that supports CUDA 12.8 Pick 1 or 2 L40S GPU(s) -![RunPod CUDA selection](../../_static/images/runpod_cuda.png) +![RunPod CUDA selection](../../assets/images/runpod_cuda.png) When creating your pod template, use this image: @@ -24,11 +23,11 @@ Paste Container Start Command to support SSH ([RunPod Docs](https://docs.runpod. bash -c "apt update;DEBIAN_FRONTEND=noninteractive apt-get install openssh-server -y;mkdir -p ~/.ssh;cd $_;chmod 700 ~/.ssh;echo \"$PUBLIC_KEY\" >> authorized_keys;chmod 700 authorized_keys;service ssh start;sleep infinity" ``` -![RunPod template configuration](../../_static/images/runpod_template.png) +![RunPod template configuration](../../assets/images/runpod_template.png) After deploying, the pod will take a few minutes to pull the image and start the SSH service. -![RunPod ssh](../../_static/images/runpod_ssh.png) +![RunPod ssh](../../assets/images/runpod_ssh.png) ## Working with the pod diff --git a/docs/source/contributing/overview.md b/docs/contributing/overview.md similarity index 98% rename from docs/source/contributing/overview.md rename to docs/contributing/overview.md index eee1d80c5..27a07ceb9 100644 --- a/docs/source/contributing/overview.md +++ b/docs/contributing/overview.md @@ -1,4 +1,3 @@ -(developer-overview)= # 🛠️ Contributing to FastVideo diff --git a/docs/source/contributing/profiling.md b/docs/contributing/profiling.md similarity index 100% rename from docs/source/contributing/profiling.md rename to docs/contributing/profiling.md diff --git a/docs/source/design/overview.md b/docs/design/overview.md similarity index 97% rename from docs/source/design/overview.md rename to docs/design/overview.md index b38ebbbd8..49c53f694 100644 --- a/docs/source/design/overview.md +++ b/docs/design/overview.md @@ -29,7 +29,6 @@ FastVideo separates model components from execution logic with these principles: - **Custom Attention Backends**: Components can support and use different Attention implementations - **Pipeline Abstraction**: Consistent interface across diffusion models -(design-fastvideo-args)= ## FastVideoArgs The `FastVideoArgs` class in `fastvideo/fastvideo_args.py` serves as the central configuration system for FastVideo. It contains all parameters needed to control model loading, inference configuration, performance optimization settings, and more. @@ -61,7 +60,6 @@ with set_current_fastvideo_args(fastvideo_args): result = generate_video() ``` -(design-pipeline-system)= ## Pipeline System ### `ComposedPipelineBase` @@ -108,7 +106,6 @@ def forward(self, batch: ForwardBatch, fastvideo_args: FastVideoArgs) -> Forward return batch ``` -(design-forwardbatch)= ### ForwardBatch Defined in `fastvideo/pipelines/pipeline_batch_info.py`, `ForwardBatch` encapsulates the data payload passed between pipeline stages. It typically holds: @@ -120,12 +117,10 @@ Defined in `fastvideo/pipelines/pipeline_batch_info.py`, `ForwardBatch` encapsul This structure facilitates clear state transitions between stages. -(design-model-components)= ## Model Components The `fastvideo/models/` directory contains implementations of the core neural network models used in video diffusion: -(design-transformer-models)= ### Transformer Models Transformer networks perform the actual denoising during diffusion: @@ -152,7 +147,6 @@ def forward( return noise_pred # Predicted noise residual ``` -(design-vae-variational-auto-encoder)= ### VAE (Variational Auto-Encoder) VAEs handle conversion between pixel space and latent space: @@ -170,7 +164,6 @@ FastVideo's VAE implementations include: - Optional tiling for large frames - Distributed weight support -(design-text-and-image-encoders)= ### Text and Image Encoders Encoders process conditioning inputs into embeddings: @@ -188,7 +181,6 @@ FastVideo implements optimizations such as: - Caching for common prompts - Precision-tuned computation -(design-schedulers)= ### Schedulers Schedulers manage the diffusion sampling process: @@ -216,7 +208,6 @@ def step( return prev_sample ``` -(design-optimized-attention)= ## Optimized Attention The `fastvideo/attention/` directory contains optimized attention implementations crucial for efficient video diffusion: @@ -245,12 +236,10 @@ Supports various patterns with memory optimization techniques: - **Cross/Self/Temporal/Global-Local Attention** - Chunking, progressive computation, optimized masking -(design-distributed-processing)= ## Distributed Processing The `fastvideo/distributed/` directory contains implementations for distributed model execution: -(design-tensor-parallelism)= ### Tensor Parallelism Tensor parallelism splits model weights across devices: @@ -307,7 +296,6 @@ Efficient communication primitives minimize distributed overhead: - **Tensor-Parallel AllReduce**: Combines partial results - **Distributed Synchronization**: Coordinates execution -(design-forwardcontext)= ## Forward Context Management ### ForwardContext @@ -330,7 +318,6 @@ with set_forward_context(current_timestep, attn_metadata, fastvideo_args): output = model(inputs) ``` -(design-executor-and-worker-abstractions)= ## Executor and Worker System The `fastvideo/worker/` directory contains the distributed execution framework: @@ -357,7 +344,6 @@ Each GPU worker: This design allows FastVideo to efficiently utilize multiple GPUs while providing a simple, unified interface for model execution. -(design-platforms)= ## Platforms The `fastvideo/platforms/` directory provides hardware platform abstractions that enable FastVideo to run efficiently on different hardware configurations: @@ -388,7 +374,6 @@ else: The platform system is designed to be extensible for future hardware targets. -(design-logger)= ## Logger See [PR](https://github.com/hao-ai-lab/FastVideo/pull/356) diff --git a/docs/source/distillation/data_preprocess.md b/docs/distillation/data_preprocess.md similarity index 98% rename from docs/source/distillation/data_preprocess.md rename to docs/distillation/data_preprocess.md index 9388d804d..0b0824a55 100644 --- a/docs/source/distillation/data_preprocess.md +++ b/docs/distillation/data_preprocess.md @@ -1,4 +1,3 @@ -(v0-data-preprocess)= # 🧱 Data Preprocess for Distillation diff --git a/docs/source/distillation/dmd.md b/docs/distillation/dmd.md similarity index 100% rename from docs/source/distillation/dmd.md rename to docs/distillation/dmd.md diff --git a/docs/examples/examples_index.md b/docs/examples/examples_index.md new file mode 100644 index 000000000..d77a201bb --- /dev/null +++ b/docs/examples/examples_index.md @@ -0,0 +1,12 @@ +# 💡 Examples + +A collection of examples demonstrating usage of FastVideo. + +All documented examples are autogenerated using [generate_examples.py](https://github.com/hao-ai-lab/FastVideo/blob/main/docs/generate_examples.py) from examples found in the [examples](https://github.com/hao-ai-lab/FastVideo/tree/main/examples) directory. + +## Examples + +- [Examples Distillation Index](distillation/examples/examples_distillation_index.md) +- [Examples Training Index](training/examples/examples_training_index.md) +- [Examples Inference Index](inference/examples/examples_inference_index.md) + diff --git a/docs/source/generate_examples.py b/docs/generate_examples.py similarity index 76% rename from docs/source/generate_examples.py rename to docs/generate_examples.py index a2e4f6696..500f41759 100644 --- a/docs/source/generate_examples.py +++ b/docs/generate_examples.py @@ -6,10 +6,11 @@ import re from dataclasses import dataclass, field from pathlib import Path -ROOT_DIR = Path(__file__).parent.parent.parent.resolve() -ROOT_DIR_RELATIVE = '../../../..' +ROOT_DIR = Path(__file__).parent.parent.resolve() +ROOT_DIR_RELATIVE = '../..' EXAMPLE_DIR = ROOT_DIR / "examples" -EXAMPLE_DOC_DIR = ROOT_DIR / "docs/source/getting_started/examples" +EXAMPLE_DOC_DIR = ROOT_DIR / "docs/getting_started/examples" +GITHUB_REPO = "hao-ai-lab/FastVideo" # Update this to your repo def fix_case(text: str) -> str: @@ -71,9 +72,16 @@ class Index: def generate(self) -> str: content = f"# {self.title}\n\n{self.description}\n\n" - content += ":::{toctree}\n" - content += f":caption: {self.caption}\n:maxdepth: {self.maxdepth}\n" - content += "\n".join(self.documents) + "\n:::\n" + if self.caption: + content += f"## {self.caption}\n\n" + # Generate a simple list of links for MkDocs + for doc in self.documents: + # Convert document path to proper link + doc_link = doc.replace("\\", "/") + # Get just the filename for the link text + doc_title = fix_case(Path(doc).stem.replace("_", " ").title()) + content += f"- [{doc_title}]({doc_link}.md)\n" + content += "\n" return content @@ -142,30 +150,66 @@ class Example: return fix_case(self.path.stem.replace("_", " ").title()) def generate(self) -> str: - # Convert the path to a relative path from __file__ - make_relative = lambda path: ROOT_DIR_RELATIVE / path.relative_to( - ROOT_DIR) + # Create GitHub link to source + github_path = str(self.path.relative_to(ROOT_DIR)).replace("\\", "/") + github_url = f"https://github.com/{GITHUB_REPO}/blob/main/{github_path}" + content = f"**Source:** [{github_path}]({github_url})\n\n" - content = f"Source .\n\n" - include = "include" if self.main_file.suffix == ".md" else \ - "literalinclude" - if include == "literalinclude": + # Add title for code files + if self.main_file.suffix != ".md": content += f"# {self.title}\n\n" - content += f":::{{{include}}} {make_relative(self.main_file)}\n" # type: ignore[no-untyped-call] - if include == "literalinclude": - content += f":language: {self.main_file.suffix[1:]}\n" - content += ":::\n\n" + + # Include main file content + if self.main_file.suffix == ".md": + # For markdown files, include the content directly + with open(self.main_file, encoding='utf-8') as f: + content += f.read() + "\n\n" + else: + # For code files, use code blocks + language = self.main_file.suffix[1:] if self.main_file.suffix else "" + with open(self.main_file, encoding='utf-8') as f: + file_content = f.read() + content += f"```{language}\n{file_content}\n```\n\n" if not self.other_files: return content - content += "## Example materials\n\n" + content += "## Additional Files\n\n" + # Define binary/non-text file extensions to skip + binary_extensions = { + '.mp4', '.avi', '.mov', '.mkv', '.gif', '.jpg', '.jpeg', '.png', + '.webp', '.bmp', '.pdf', '.zip', '.tar', '.gz', '.mp3', '.wav' + } + for file in sorted(self.other_files): - include = "include" if file.suffix == ".md" else "literalinclude" - content += f":::{{admonition}} {file.relative_to(self.path)}\n" - content += ":class: dropdown\n\n" - content += f":::{{{include}}} {make_relative(file)}\n:::\n" # type: ignore[no-untyped-call] - content += ":::\n\n" + # Skip binary files + if file.suffix.lower() in binary_extensions: + continue + + file_rel_path = file.relative_to(self.path) + # Use collapsible admonition syntax for MkDocs + content += f"??? note \"{file_rel_path}\"\n\n" + + try: + if file.suffix == ".md": + # Include markdown content with indentation + with open(file, encoding='utf-8') as f: + for line in f: + content += f" {line}" + else: + # Include code with proper formatting + language = file.suffix[1:] if file.suffix else "" + with open(file, encoding='utf-8') as f: + file_content = f.read() + # Indent the code block for the admonition + content += f" ```{language}\n" + for line in file_content.split('\n'): + content += f" {line}\n" + content += " ```\n" + content += "\n" + except UnicodeDecodeError: + # Skip files that can't be decoded as UTF-8 + continue return content @@ -195,7 +239,7 @@ class NestedStructure: def create_category_indices() -> dict[str, Index]: """Create category indices with their respective configurations.""" - main_index_dir = ROOT_DIR / "docs/source/examples" + main_index_dir = ROOT_DIR / "docs/examples" if not main_index_dir.exists(): main_index_dir.mkdir(parents=True) @@ -203,17 +247,16 @@ def create_category_indices() -> dict[str, Index]: "inference": Index( path=ROOT_DIR / - "docs/source/inference/examples/examples_inference_index.md", + "docs/inference/examples/examples_inference_index.md", title="🚀 Examples", description= - "Inference examples demonstrate how to use FastVideo inference. We recommend starting with .", + "Inference examples demonstrate how to use FastVideo inference. We recommend starting with [basic.md](basic.md).", caption="Examples", maxdepth=1, ), "training": Index( - path=ROOT_DIR / - "docs/source/training/examples/examples_training_index.md", + path=ROOT_DIR / "docs/training/examples/examples_training_index.md", title="🚀 Examples", description= "Training examples demonstrate how to use FastVideo training.", @@ -223,7 +266,7 @@ def create_category_indices() -> dict[str, Index]: "distillation": Index( path=ROOT_DIR / - "docs/source/distillation/examples/examples_distillation_index.md", + "docs/distillation/examples/examples_distillation_index.md", title="🚀 Examples", description= "Distillation examples demonstrate how to use FastVideo distillation.", @@ -246,9 +289,21 @@ def find_examples(category_indices: dict[str, Index], examples = [] glob_patterns = ["*.py", "*.md", "*.sh"] + # Map category names to actual directory names + category_dir_mapping = { + "distillation": "distill", # examples/distill/ -> distillation category + } + # Find categorised examples for category in category_indices: - category_dir = EXAMPLE_DIR / category + # Use mapped directory name if available, otherwise use category name + dir_name = category_dir_mapping.get(category, category) + category_dir = EXAMPLE_DIR / dir_name + + # Skip if directory doesn't exist + if not category_dir.exists(): + continue + globs = [category_dir.glob(pattern) for pattern in glob_patterns] for path in itertools.chain(*globs): examples.append(Example(path, category)) @@ -279,11 +334,18 @@ def create_nested_structures( dict[str, NestedStructure]]]] = {} + # Map category names to actual directory names + category_dir_mapping = { + "distillation": "distill", + } + for example in examples: if example.category not in ["training", "distillation"]: continue - category_dir = EXAMPLE_DIR / example.category + # Use mapped directory name if available + dir_name = category_dir_mapping.get(example.category, example.category) + category_dir = EXAMPLE_DIR / dir_name relative_path = example.path.relative_to(category_dir) path_parts = relative_path.parts @@ -415,7 +477,7 @@ def generate_nested_examples(nested_structures: dict[str, dict[str, dict[ category_index.documents.append(method) -def generate_examples(generate_main_index=False): +def generate_examples(generate_main_index: bool = False) -> None: """ Generate example documentation. @@ -429,12 +491,14 @@ def generate_examples(generate_main_index=False): # Create the main examples index only if requested examples_index = None if generate_main_index: - main_index_dir = ROOT_DIR / "docs/source/examples" + main_index_dir = ROOT_DIR / "docs/examples" examples_index = Index( path=main_index_dir / "examples_index.md", title="💡 Examples", description= - "A collection of examples demonstrating usage of FastVideo.\nAll documented examples are autogenerated using from examples found in .", + "A collection of examples demonstrating usage of FastVideo.\n\n" + f"All documented examples are autogenerated using [generate_examples.py](https://github.com/{GITHUB_REPO}/blob/main/docs/generate_examples.py) " + f"from examples found in the [examples](https://github.com/{GITHUB_REPO}/tree/main/examples) directory.", caption="Examples", maxdepth=2) @@ -471,3 +535,19 @@ def generate_examples(generate_main_index=False): if generate_main_index and examples_index: with open(examples_index.path, "w+") as f: f.write(examples_index.generate()) + + +def on_pre_build_hook(config, **kwargs): + """ + MkDocs hook to generate examples before building the documentation. + This function is called automatically by the mkdocs-simple-hooks plugin. + """ + print("Generating example documentation...") + generate_examples(generate_main_index=True) + print("Example documentation generated successfully!") + + +if __name__ == "__main__": + print("Generating example documentation...") + generate_examples(generate_main_index=True) + print("Example documentation generated successfully!") diff --git a/docs/getting_started/installation.md b/docs/getting_started/installation.md new file mode 100644 index 000000000..6961a5d26 --- /dev/null +++ b/docs/getting_started/installation.md @@ -0,0 +1,41 @@ + +# 🔧 Installation + +FastVideo supports the following hardware platforms: + +- [NVIDIA CUDA](installation/gpu.md) +- [Apple silicon](installation/mps.md) + +## Quick Installation + +### Using pip + +```bash +pip install fastvideo +``` + +### Using conda + +```bash +conda install -c conda-forge fastvideo +``` + +### From source + +```bash +git clone https://github.com/hao-ai-lab/FastVideo.git +cd FastVideo +pip install -e . +``` + +## Hardware Requirements + +- **NVIDIA GPUs**: CUDA 11.8+ with compute capability 7.0+ +- **Apple Silicon**: macOS 12.0+ with M1/M2/M3 chips +- **CPU**: x86_64 architecture (for CPU-only inference) + +## Next Steps + +- [Quick Start Guide](quick_start.md) - Get started with your first video generation +- [Configuration](../inference/configuration.md) - Learn about configuration options +- [Examples](../inference/examples/) - Explore example scripts and notebooks diff --git a/docs/source/getting_started/installation/gpu.md b/docs/getting_started/installation/gpu.md similarity index 86% rename from docs/source/getting_started/installation/gpu.md rename to docs/getting_started/installation/gpu.md index 5b663d3fe..9d090b0bd 100644 --- a/docs/source/getting_started/installation/gpu.md +++ b/docs/getting_started/installation/gpu.md @@ -30,17 +30,8 @@ conda create -n fastvideo python=3.12 -y conda activate fastvideo ``` -:::{note} -[PyTorch has deprecated the conda release channel](https://github.com/pytorch/pytorch/issues/138506). If you use `conda`, please only use it to create Python environment rather than installing packages. -::: - #### uv -:::{tip} -We highly recommend using `uv` to install FastVideo. In our experience, `uv` speeds up installation by at least 3x. -Note that you can also use `uv` to install FastVideo in a Conda environment. -::: - Or you can create a new Python environment using [uv](https://docs.astral.sh/uv/), a very fast Python environment manager. Please follow the [documentation](https://docs.astral.sh/uv/#getting-started) to install `uv`. After installing `uv`, you can create a new Python environment using the following command: ```console diff --git a/docs/source/getting_started/installation/mps.md b/docs/getting_started/installation/mps.md similarity index 84% rename from docs/source/getting_started/installation/mps.md rename to docs/getting_started/installation/mps.md index 7f4091885..68b348872 100644 --- a/docs/source/getting_started/installation/mps.md +++ b/docs/getting_started/installation/mps.md @@ -31,17 +31,8 @@ conda create -n fastvideo python=3.12.4 -y conda activate fastvideo ``` -:::{note} -[PyTorch has deprecated the conda release channel](https://github.com/pytorch/pytorch/issues/138506). If you use `conda`, please only use it to create Python environment rather than installing packages. -::: - #### uv -:::{tip} -We highly recommend using `uv` to install FastVideo. In our experience, `uv` speeds up installation by at least 3x. -Note that you can also use `uv` to install FastVideo in a Conda environment. -::: - Or you can create a new Python environment using [uv](https://docs.astral.sh/uv/), a very fast Python environment manager. Please follow the [documentation](https://docs.astral.sh/uv/#getting-started) to install `uv`. After installing `uv`, you can create a new Python environment using the following command: ```console diff --git a/docs/getting_started/quick_start.md b/docs/getting_started/quick_start.md new file mode 100644 index 000000000..f6db8620f --- /dev/null +++ b/docs/getting_started/quick_start.md @@ -0,0 +1,55 @@ +# 🚀 Quick Start + +Get up and running with FastVideo in minutes! + +## Installation + +First, install FastVideo: + +```bash +pip install fastvideo +``` + +## Basic Usage + +### Text-to-Video Generation + +```python +from fastvideo import FastVideoPipeline + +# Initialize the pipeline +pipe = FastVideoPipeline.from_pretrained("wan2.1-t2v-1.3B") + +# Generate a video +prompt = "A cat playing with a ball of yarn" +video = pipe(prompt, num_frames=16, height=512, width=512) + +# Save the video +video.save("output.mp4") +``` + +### Image-to-Video Generation + +```python +from fastvideo import FastVideoPipeline +from PIL import Image + +# Load an image +image = Image.open("input.jpg") + +# Initialize the pipeline +pipe = FastVideoPipeline.from_pretrained("wan2.1-i2v-14B-480p") + +# Generate a video from the image +video = pipe(image, num_frames=16, height=480, width=480) + +# Save the video +video.save("output.mp4") +``` + +## Next Steps + +- [Installation Guide](installation.md) - Detailed installation instructions +- [Configuration](../inference/configuration.md) - Learn about configuration options +- [Examples](../inference/examples/) - Explore more examples +- [Optimizations](../inference/optimizations.md) - Performance optimization tips diff --git a/docs/getting_started/v1_api.md b/docs/getting_started/v1_api.md new file mode 100644 index 000000000..52292fd66 --- /dev/null +++ b/docs/getting_started/v1_api.md @@ -0,0 +1,42 @@ +# V1 API + +FastVideo's V1 API provides a streamlined interface for video generation tasks with powerful customization options. This page documents the primary components of the API. + +## Video Generator + +This class will be the primary Python API for generating videos and images. + +::: fastvideo.entrypoints.video_generator.VideoGenerator + options: + show_root_heading: true + show_source: false + members: + - from_pretrained + heading_level: 3 + +`VideoGenerator.from_pretrained()` should be the primary way of creating a new video generator. + +## Configuring FastVideo + +The following two classes `PipelineConfig` and `SamplingParam` are used to configure initialization and sampling parameters, respectively. + +### PipelineConfig + +::: fastvideo.configs.pipelines.base.PipelineConfig + options: + show_root_heading: true + show_source: false + members: + - from_pretrained + - dump_to_json + heading_level: 4 + +### SamplingParam + +::: fastvideo.configs.sample.base.SamplingParam + options: + show_root_heading: true + show_source: false + members: + - from_pretrained + heading_level: 4 diff --git a/docs/source/index.md b/docs/index.md similarity index 54% rename from docs/source/index.md rename to docs/index.md index 9027a8429..8499f0004 100644 --- a/docs/source/index.md +++ b/docs/index.md @@ -1,30 +1,24 @@ # Welcome to FastVideo -:::{figure} ../../assets/logos/logo.svg -:align: center -:alt: FastVideo -:class: no-scaled-link -:width: 60% -::: +

-:::{raw} html -

-FastVideo is a unified inference and post-training framework for accelerated video generation. - -

+
+FastVideo is a unified inference and post-training framework for accelerated video generation. +
-

+

Star Watch Fork -

-::: +
FastVideo is an inference and post-training framework for diffusion models. It features an end-to-end unified pipeline for accelerating diffusion models, starting from data preprocessing to model training, finetuning, distillation, and inference. FastVideo is designed to be modular and extensible, allowing users to easily add new optimizations and techniques. Whether it is training-free optimizations or post-training optimizations, FastVideo has you covered.
- +
## Key Features @@ -42,91 +36,16 @@ FastVideo has the following features: ## Documentation -% How to start using FastVideo? +Welcome to FastVideo! This documentation will help you get started with our unified inference and post-training framework for accelerated video generation. -:::{toctree} -:caption: Getting Started -:maxdepth: 1 +Use the navigation menu on the left to explore different sections: -getting_started/installation - -::: - -:::{toctree} -:caption: Inference -:maxdepth: 1 - -inference/inference_quick_start -inference/examples/examples_inference_index -inference/configuration -inference/optimizations -inference/comfyui -inference/support_matrix -inference/cli -inference/add_pipeline -::: - -:::{toctree} -:caption: Training -:maxdepth: 1 - -training/examples/examples_training_index -training/data_preprocess - -::: - -:::{toctree} -:caption: Distillation -:maxdepth: 1 - -distillation/examples/examples_distillation_index -distillation/data_preprocess -distillation/dmd -::: - -% What is STA Kernel? - -:::{toctree} -:caption: Sliding Tile Attention -:maxdepth: 1 - -sliding_tile_attention/installation -sliding_tile_attention/demo -::: - -% What is VSA Kernel? - -:::{toctree} -:caption: Video Sparse Attention -:maxdepth: 1 - -video_sparse_attention/installation -::: - -:::{toctree} -:caption: Design -:maxdepth: 1 -design/overview -::: - -:::{toctree} -:caption: Developer Guide -:maxdepth: 2 - -contributing/overview -contributing/developer_env/index -contributing/profiling -::: - -:::{toctree} -:caption: API Reference -:maxdepth: 2 - - -api/fastvideo/fastvideo -::: - -## Indices and tables - -- {ref}`genindex` -- {ref}`modindex` +- **Getting Started**: Installation and quick start guides +- **Inference**: Learn how to use FastVideo for video generation +- **Training**: Data preprocessing and fine-tuning workflows +- **Distillation**: Post-training optimization techniques +- **Sliding Tile Attention**: Advanced attention mechanisms +- **Video Sparse Attention**: Efficient attention for video models +- **Design**: Framework architecture and design principles +- **Developer Guide**: Contributing and development setup +- **API Reference**: Complete API documentation diff --git a/docs/source/inference/add_pipeline.md b/docs/inference/add_pipeline.md similarity index 99% rename from docs/source/inference/add_pipeline.md rename to docs/inference/add_pipeline.md index dafa5fadf..5cf1501e7 100644 --- a/docs/source/inference/add_pipeline.md +++ b/docs/inference/add_pipeline.md @@ -1,4 +1,3 @@ -(add-pipeline)= # 🏗️ Adding a New Pipeline diff --git a/docs/source/inference/cli.md b/docs/inference/cli.md similarity index 100% rename from docs/source/inference/cli.md rename to docs/inference/cli.md diff --git a/docs/source/inference/comfyui.md b/docs/inference/comfyui.md similarity index 100% rename from docs/source/inference/comfyui.md rename to docs/inference/comfyui.md diff --git a/docs/source/inference/configuration.md b/docs/inference/configuration.md similarity index 98% rename from docs/source/inference/configuration.md rename to docs/inference/configuration.md index 6462931d0..feb8c594c 100644 --- a/docs/source/inference/configuration.md +++ b/docs/inference/configuration.md @@ -1,4 +1,4 @@ -(inference-configuration)= + # Configuration ## Multi-GPU Setup diff --git a/docs/source/inference/inference_quick_start.md b/docs/inference/inference_quick_start.md similarity index 100% rename from docs/source/inference/inference_quick_start.md rename to docs/inference/inference_quick_start.md diff --git a/docs/source/inference/optimizations.md b/docs/inference/optimizations.md similarity index 94% rename from docs/source/inference/optimizations.md rename to docs/inference/optimizations.md index 0774bf500..ec8bc69a0 100644 --- a/docs/source/inference/optimizations.md +++ b/docs/inference/optimizations.md @@ -1,4 +1,3 @@ -(inference-optimizations)= # Optimizations @@ -16,8 +15,6 @@ This page describes the various options for speeding up generation times in Fast - Caching Techniques - [TeaCache](#optimizations-teacache) -(optimizations-backends)= - ## Attention Backends ### Available Backends @@ -49,8 +46,6 @@ You can also set the environment variable on the command line: FASTVIDEO_ATTENTION_BACKEND=SAGE_ATTN python example.py ``` -(optimizations-flash)= - ### Flash Attention **`FLASH_ATTN`** @@ -71,12 +66,6 @@ pip install ninja python setup.py install ``` -:::{note} -FastVideo will automatically detect and use `FA3` if it is installed when using `FLASH_ATTN` backend. -::: - -(optimizations-sta)= - ### Sliding Tile Attention **`SLIDING_TILE_ATTN`** @@ -87,8 +76,6 @@ pip install st_attn==0.0.4 Please see [this page](#sta-installation) for more installation instructions. -(optimizations-vsa)= - ### Video Sparse Attention **`VIDEO_SPARSE_ATTN`** @@ -100,8 +87,6 @@ python setup_vsa.py install Please see [this page](#vsa-installation) for more installation instructions. -(optimizations-sage)= - ### Sage Attention **`SAGE_ATTN`** @@ -114,8 +99,6 @@ cd sageattention python setup.py install # or pip install -e . ``` -(optimizations-sage3)= - ### Sage Attention 3 **`SAGE_ATTN_THREE`** @@ -136,8 +119,6 @@ To use Sage Attention 3 in FastVideo, first get access to the SageAttention3 cod python setup.py install ``` -(optimizations-teacache)= - ## Teacache TeaCache is an optimization technique supported in FastVideo that can significantly speed up video generation by skipping redundant calculations across diffusion steps. This guide explains how to enable and configure TeaCache for optimal performance in FastVideo. diff --git a/docs/inference/support_matrix.md b/docs/inference/support_matrix.md new file mode 100644 index 000000000..d207d80ca --- /dev/null +++ b/docs/inference/support_matrix.md @@ -0,0 +1,66 @@ +# Compatibility Matrix + +The table below shows every supported model and optimizations supported for them. + +The symbols used have the following meanings: + +- ✅ = Full compatibility +- ❌ = No compatibility +- ⭕ = Does not apply to this model + +## Models x Optimization + +The `HuggingFace Model ID` can be directly pass to `from_pretrained()` methods and FastVideo will use the optimal default parameters when initializing and generating videos. + + + +| Model Name | HuggingFace Model ID | Resolutions | TeaCache | Sliding Tile Attn | Sage Attn | VSA | +|------------|---------------------|-------------|----------|-------------------|-----------|-----| +| FastWan2.1 T2V 1.3B | `FastVideo/FastWan2.1-T2V-1.3B-Diffusers` | 480P | ⭕ | ⭕ | ⭕ | ✅ | +| FastWan2.2 TI2V 5B Full Attn* | `FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers` | 720P | ⭕ | ⭕ | ⭕ | ✅ | +| Wan2.2 TI2V 5B | `Wan-AI/Wan2.2-TI2V-5B-Diffusers` | 720P | ⭕ | ⭕ | ✅ | ⭕ | +| Wan2.2 T2V A14B | `Wan-AI/Wan2.2-T2V-A14B-Diffusers` | 480P
720P | ❌ | ❌ | ✅ | ⭕ | +| Wan2.2 I2V A14B | `Wan-AI/Wan2.2-I2V-A14B-Diffusers` | 480P
720P | ❌ | ❌ | ✅ | ⭕ | +| HunyuanVideo | `hunyuanvideo-community/HunyuanVideo` | 720px1280p
544px960p | ❌ | ✅ | ✅ | ⭕ | +| FastHunyuan | `FastVideo/FastHunyuan-diffusers` | 720px1280p
544px960p | ❌ | ✅ | ✅ | ⭕ | +| Wan2.1 T2V 1.3B | `Wan-AI/Wan2.1-T2V-1.3B-Diffusers` | 480P | ✅ | ✅* | ✅ | ⭕ | +| Wan2.1 T2V 14B | `Wan-AI/Wan2.1-T2V-14B-Diffusers` | 480P, 720P | ✅ | ✅* | ✅ | ⭕ | +| Wan2.1 I2V 480P | `Wan-AI/Wan2.1-I2V-14B-480P-Diffusers` | 480P | ✅ | ✅* | ✅ | ⭕ | +| Wan2.1 I2V 720P | `Wan-AI/Wan2.1-I2V-14B-720P-Diffusers` | 720P | ✅ | ✅ | ✅ | ⭕ | +| StepVideo T2V | `FastVideo/stepvideo-t2v-diffusers` | 768px768px204f
544px992px204f
544px992px136f | ❌ | ❌ | ✅ | ⭕ | + +**Note**: Wan2.2 TI2V 5B has some quality issues when performing I2V generation. We are working on fixing this issue. + +## Special requirements + +### StepVideo T2V +- The self-attention in text-encoder (step_llm) only supports CUDA capabilities sm_80 sm_86 and sm_90 + +### Sliding Tile Attention +- Currently only Hopper GPUs (H100s) are supported. diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 747ffb7b3..000000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -if "%1" == "" goto help - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt deleted file mode 100644 index 07613d669..000000000 --- a/docs/requirements-docs.txt +++ /dev/null @@ -1,15 +0,0 @@ -sphinx==7.4.7 -sphinx-argparse==0.5.2 -sphinx-autodoc2==0.5.0 -sphinx-book-theme==1.1.4 -sphinx-copybutton==0.5.2 -sphinx-design==0.6.1 -sphinx-togglebutton==0.3.2 -myst-parser==3.0.1 -msgspec -commonmark # Required by sphinx-argparse when using :markdownhelp: - -# packages to install to build the documentation -cachetools -# -f https://download.pytorch.org/whl/cpu -torch \ No newline at end of file diff --git a/docs/source/sliding_tile_attention/demo.md b/docs/sliding_tile_attention/demo.md similarity index 97% rename from docs/source/sliding_tile_attention/demo.md rename to docs/sliding_tile_attention/demo.md index dadae81db..080b8e24c 100644 --- a/docs/source/sliding_tile_attention/demo.md +++ b/docs/sliding_tile_attention/demo.md @@ -1,4 +1,3 @@ -(sta-demo)= # 🔍 Demo This is is a demo for 2D STA with window size (6,6) operating on a (10, 10) image. diff --git a/docs/source/sliding_tile_attention/installation.md b/docs/sliding_tile_attention/installation.md similarity index 98% rename from docs/source/sliding_tile_attention/installation.md rename to docs/sliding_tile_attention/installation.md index 8e9f1daec..0ed54f3db 100644 --- a/docs/source/sliding_tile_attention/installation.md +++ b/docs/sliding_tile_attention/installation.md @@ -1,4 +1,3 @@ -(sta-installation)= # 🔧 Installation You can install the Sliding Tile Attention package using diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css deleted file mode 100644 index 79bd2082b..000000000 --- a/docs/source/_static/custom.css +++ /dev/null @@ -1,8 +0,0 @@ -.vertical-table-header th.head:not(.stub) { - writing-mode: sideways-lr; - white-space: nowrap; - max-width: 0; - p { - margin: 0; - } -} diff --git a/docs/source/_templates/sections/header.html b/docs/source/_templates/sections/header.html deleted file mode 100644 index 80bc371c6..000000000 --- a/docs/source/_templates/sections/header.html +++ /dev/null @@ -1,39 +0,0 @@ - - - diff --git a/docs/source/api/summary.md b/docs/source/api/summary.md deleted file mode 100644 index 4b5cdd89d..000000000 --- a/docs/source/api/summary.md +++ /dev/null @@ -1,19 +0,0 @@ -# Summary - -## Video Generator - -```{autodoc2-summary} - fastvideo.VideoGenerator -``` - -## Initialization Configuration - -```{autodoc2-summary} - fastvideo.configs.pipelines.PipelineConfig -``` - -## Sampling Configuration - -```{autodoc2-summary} - fastvideo.configs.sample.SamplingParam -``` diff --git a/docs/source/autodoc2_docstring_parser.py b/docs/source/autodoc2_docstring_parser.py deleted file mode 100644 index 32f25d3c8..000000000 --- a/docs/source/autodoc2_docstring_parser.py +++ /dev/null @@ -1,22 +0,0 @@ -# type: ignore -# SPDX-License-Identifier: Apache-2.0 -from docutils import nodes -from myst_parser.parsers.sphinx_ import MystParser -from sphinx.ext.napoleon import docstring - - -class NapoleonParser(MystParser): - - def parse(self, input_string: str, document: nodes.document) -> None: - # Get the Sphinx configuration - config = document.settings.env.config - - parsed_content = str( - docstring.GoogleDocstring( - str(docstring.NumpyDocstring(input_string, config)), - config, - )) - return super().parse(parsed_content, document) - - -Parser = NapoleonParser diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index 12bd335d4..000000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,275 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 - -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. - -import datetime -import logging -import os -import re -import sys -from pathlib import Path - -import requests - -logger = logging.getLogger(__name__) -REPO_ROOT = Path(__file__).resolve().parent.parent.parent -print(os.path.abspath(REPO_ROOT)) -sys.path.append(os.path.abspath(REPO_ROOT)) - -# -- Project information ----------------------------------------------------- - -project = 'FastVideo' -copyright = f'{datetime.datetime.now().year}, FastVideo Team' -author = 'the FastVideo Team' - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "sphinx.ext.napoleon", - "sphinx.ext.linkcode", - "sphinx.ext.intersphinx", - "sphinx_copybutton", - "autodoc2", - "myst_parser", - "sphinxarg.ext", - "sphinx_design", - "sphinx_togglebutton", -] -myst_enable_extensions = [ - "colon_fence", - "fieldlist", -] -autodoc2_packages = [ - { - "path": "../../fastvideo", - "exclude_dirs": ["__pycache__", "third_party"], - }, -] -autodoc2_output_dir = "api" -autodoc2_render_plugin = "myst" -autodoc2_hidden_objects = ["dunder", "private", "inherited"] -autodoc2_docstring_parser_regexes = [ - (".*", "docs.source.autodoc2_docstring_parser"), -] -autodoc2_sort_names = True -autodoc2_index_template = None -autodoc2_skip_module_regexes = [ - "fastvideo.dataset", - "fastvideo.distill", - "fastvideo.data_preprocess", - "fastvideo.models", - "fastvideo.sample", - "fastvideo.utils", - "fastvideo.distill_adv", - "fastvideo.train", -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns: list[str] = ["**/*.template.md", "**/*.inc.md"] - -# Exclude the prompt "$" when copying code -copybutton_prompt_text = r"\$ " -copybutton_prompt_is_regexp = True - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_title = project -html_theme = 'sphinx_book_theme' -html_logo = '../../assets/logos/icon_simple.svg' -html_theme_options = { - 'path_to_docs': 'docs/source', - 'repository_url': 'https://github.com/hao-ai-lab/FastVideo/', - 'use_repository_button': True, - 'use_edit_page_button': True, - # Prevents the full API being added to the left sidebar of every page. - # Reduces build time by 2.5x and reduces build size from ~225MB to ~95MB. - 'collapse_navbar': True, - # Makes API visible in the right sidebar on API reference pages. - 'show_toc_level': 3, -} -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] -html_js_files = ["custom.js"] -html_css_files = ["custom.css"] - -myst_url_schemes = { - 'http': None, - 'https': None, - 'mailto': None, - 'ftp': None, - "gh-issue": { - "url": - "https://github.com/hao-ai-lab/FastVideo/issues/{{path}}#{{fragment}}", - "title": "Issue #{{path}}", - "classes": ["github"], - }, - "gh-pr": { - "url": - "https://github.com/hao-ai-lab/FastVideo/pull/{{path}}#{{fragment}}", - "title": "Pull Request #{{path}}", - "classes": ["github"], - }, - "gh-dir": { - "url": "https://github.com/hao-ai-lab/FastVideo/tree/main/{{path}}", - "title": "{{path}}", - "classes": ["github"], - }, - "gh-file": { - "url": "https://github.com/hao-ai-lab/FastVideo/blob/main/{{path}}", - "title": "{{path}}", - "classes": ["github"], - }, -} - -# see https://docs.readthedocs.io/en/stable/reference/environment-variables.html # noqa -READTHEDOCS_VERSION_TYPE = os.environ.get('READTHEDOCS_VERSION_TYPE') -if READTHEDOCS_VERSION_TYPE == "tag": - # remove the warning banner if the version is a tagged release - header_file = os.path.join(os.path.dirname(__file__), - "_templates/sections/header.html") - # The file might be removed already if the build is triggered multiple times - # (readthedocs build both HTML and PDF versions separately) - if os.path.exists(header_file): - os.remove(header_file) - - -# Generate additional rst documentation here. -def setup(app): - from docs.source.generate_examples import generate_examples - generate_examples() - - -_cached_base: str = "" -_cached_branch: str = "" - - -def get_repo_base_and_branch(pr_number: str) -> tuple[str | None, str | None]: - global _cached_base, _cached_branch - if _cached_base and _cached_branch: - return _cached_base, _cached_branch - - url = f"https://api.github.com/repos/hao-ai-lab/FastVideo/pulls/{pr_number}" - response = requests.get(url) - if response.status_code == 200: - data = response.json() - _cached_base = data['head']['repo']['full_name'] - _cached_branch = data['head']['ref'] - return _cached_base, _cached_branch - else: - logger.error("Failed to fetch PR details: %s", response) - return None, None - - -def linkcode_resolve(domain, info): - if domain != 'py': - return None - if not info['module']: - return None - - # Get path from module name - file = Path(f"{info['module'].replace('.', '/')}.py") - path = REPO_ROOT / file - if not path.exists(): - path = REPO_ROOT / file.with_suffix("") / "__init__.py" - if not path.exists(): - return None - - # Get the line number of the object - with open(path) as f: - lines = f.readlines() - name = info['fullname'].split(".")[-1] - pattern = fr"^( {{4}})*((def|class) )?{name}\b.*" - for lineno, line in enumerate(lines, 1): - if not line or line.startswith("#"): - continue - if re.match(pattern, line): - break - - # If the line number is not found, return None - if lineno == len(lines): - return None - - # If the line number is found, create the URL - filename = path.relative_to(REPO_ROOT) - if "checkouts" in path.parts: - # a PR build on readthedocs - pr_number = REPO_ROOT.name - base, branch = get_repo_base_and_branch(pr_number) - if base and branch: - return f"https://github.com/{base}/blob/{branch}/{filename}#L{lineno}" - # Otherwise, link to the source file on the main branch - return f"https://github.com/hao-ai-lab/FastVideo/blob/main/{filename}#L{lineno}" - - -# Mock out external dependencies here, otherwise the autodoc pages may be blank. -autodoc_mock_imports = [ - "blake3", - "compressed_tensors", - "cpuinfo", - "cv2", - "torch", - "huggingface_hub", - "torchvision", - "transformers", - "psutil", - "prometheus_client", - "sentencepiece", - "vllm._C", - "PIL", - "numpy", - 'triton', - "tqdm", - "tensorizer", - "pynvml", - "outlines", - "xgrammar", - "librosa", - "soundfile", - "gguf", - "lark", - "decord", -] - -for mock_target in autodoc_mock_imports: - if mock_target in sys.modules: - logger.info( - "Potentially problematic mock target (%s) found; " - "autodoc_mock_imports cannot mock modules that have already " - "been loaded into sys.modules when the sphinx build starts.", - mock_target) - -intersphinx_mapping = { - "python": ("https://docs.python.org/3", None), - "typing_extensions": - ("https://typing-extensions.readthedocs.io/en/latest", None), - "aiohttp": ("https://docs.aiohttp.org/en/stable", None), - "pillow": ("https://pillow.readthedocs.io/en/stable", None), - "numpy": ("https://numpy.org/doc/stable", None), - "torch": ("https://pytorch.org/docs/stable", None), - "psutil": ("https://psutil.readthedocs.io/en/stable", None), -} - -navigation_with_keys = False diff --git a/docs/source/getting_started/installation.md b/docs/source/getting_started/installation.md deleted file mode 100644 index e8ec58705..000000000 --- a/docs/source/getting_started/installation.md +++ /dev/null @@ -1,18 +0,0 @@ -(installation-index)= - -# 🔧 Installation - -FastVideo supports the following hardware platforms: - -:::{toctree} -:maxdepth: 1 -:hidden: - -installation/gpu -installation/mps -::: - -- - - NVIDIA CUDA -- - - Apple silicon diff --git a/docs/source/getting_started/v1_api.md b/docs/source/getting_started/v1_api.md deleted file mode 100644 index 2fe3695c0..000000000 --- a/docs/source/getting_started/v1_api.md +++ /dev/null @@ -1,83 +0,0 @@ -# V1 API - -FastVideo's V1 API provides a streamlined interface for video generation tasks with powerful customization options. This page documents the primary components of the API. - -## Video Generator - -This class will be the primary Python API for generating videos and images. - -```{autodoc2-summary} - fastvideo.VideoGenerator -``` - -`````{py:class} VideoGenerator(fastvideo_args: fastvideo.fastvideo_args.FastVideoArgs, executor_class: type[fastvideo.worker.executor.Executor], log_stats: bool) -:canonical: fastvideo.entrypoints.video_generator.VideoGenerator - -```{autodoc2-docstring} fastvideo.entrypoints.video_generator.VideoGenerator -:parser: docs.source.autodoc2_docstring_parser -``` - -`VideoGenerator.from_pretrained()` should be the primary way of creating a new video generator. - -````{py:method} from_pretrained(model_path: str, device: typing.Optional[str] = None, torch_dtype: typing.Optional[torch.dtype] = None, pipeline_config: typing.Optional[typing.Union[str | fastvideo.configs.pipelines.PipelineConfig]] = None, **kwargs) -> fastvideo.entrypoints.video_generator.VideoGenerator -:canonical: fastvideo.entrypoints.video_generator.VideoGenerator.from_pretrained -:classmethod: - -```{autodoc2-docstring} fastvideo.entrypoints.video_generator.VideoGenerator.from_pretrained -:parser: docs.source.autodoc2_docstring_parser -``` - - -## Configuring FastVideo - -The follow two classes `PipelineConfig` and `SamplingParam` are used to configure initialization and sampling parameters, respectively. - -### PipelineConfig -```{autodoc2-summary} - fastvideo.PipelineConfig -``` - -`````{py:class} PipelineConfig -:canonical: fastvideo.configs.pipelines.base.PipelineConfig - -```{autodoc2-docstring} fastvideo.configs.pipelines.base.PipelineConfig -:parser: docs.source.autodoc2_docstring_parser -``` - -````{py:method} from_pretrained(model_path: str) -> fastvideo.configs.pipelines.base.PipelineConfig -:canonical: fastvideo.configs.pipelines.base.PipelineConfig.from_pretrained -:classmethod: - -```{autodoc2-docstring} fastvideo.configs.pipelines.base.PipelineConfig.from_pretrained -:parser: docs.source.autodoc2_docstring_parser -``` - - -````{py:method} dump_to_json(file_path: str) -:canonical: fastvideo.configs.pipelines.base.PipelineConfig.dump_to_json - -```{autodoc2-docstring} fastvideo.configs.pipelines.base.PipelineConfig.dump_to_json -:parser: docs.source.autodoc2_docstring_parser -``` - - -### SamplingParam - -```{autodoc2-summary} - fastvideo.SamplingParam -``` - -`````{py:class} SamplingParam -:canonical: fastvideo.configs.sample.base.SamplingParam - -```{autodoc2-docstring} fastvideo.configs.sample.base.SamplingParam -:parser: docs.source.autodoc2_docstring_parser -``` - -````{py:method} from_pretrained(model_path: str) -> fastvideo.configs.sample.base.SamplingParam -:canonical: fastvideo.configs.sample.base.SamplingParam.from_pretrained -:classmethod: - -```{autodoc2-docstring} fastvideo.configs.sample.base.SamplingParam.from_pretrained -:parser: docs.source.autodoc2_docstring_parser -``` diff --git a/docs/source/inference/support_matrix.md b/docs/source/inference/support_matrix.md deleted file mode 100644 index 63bc79b2f..000000000 --- a/docs/source/inference/support_matrix.md +++ /dev/null @@ -1,136 +0,0 @@ -(support-matrix)= -# Compatibility Matrix -The table below shows every supported model and optimizations supported for them. - -The symbols used have the following meanings: - -- ✅ = Full compatibility -- ❌ = No compatibility -- ⭕ = Does not apply to this model - -## Models x Optimization -The `HuggingFace Model ID` can be directly pass to `from_pretrained()` methods and FastVideo will use the optimal default parameters when initializing and generating videos. - -:::{raw} html - -::: - -:::{list-table} -:header-rows: 1 -:stub-columns: 3 -:widths: auto -:class: vertical-table-header - -- * Model Name - * HuggingFace Model ID - * Resolutions - * TeaCache - * Sliding Tile Attn - * Sage Attn - * Video Sparse Attention (VSA) -- * FastWan2.1 T2V 1.3B - * `FastVideo/FastWan2.1-T2V-1.3B-Diffusers` - * 480P - * ⭕ - * ⭕ - * ⭕ - * ✅ -- * FastWan2.2 TI2V 5B Full Attn* - * `FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers` - * 720P - * ⭕ - * ⭕ - * ⭕ - * ✅ -- * Wan2.2 TI2V 5B - * `Wan-AI/Wan2.2-TI2V-5B-Diffusers` - * 720P - * ⭕ - * ⭕ - * ✅ - * ⭕ -- * Wan2.2 T2V A14B - * `Wan-AI/Wan2.2-T2V-A14B-Diffusers` - * 480P
720P - * ❌ - * ❌ - * ✅ - * ⭕ -- * Wan2.2 I2V A14B - * `Wan-AI/Wan2.2-I2V-A14B-Diffusers` - * 480P
720P - * ❌ - * ❌ - * ✅ - * ⭕ -- * HunyuanVideo - * `hunyuanvideo-community/HunyuanVideo` - * 720px1280p
544px960p - * ❌ - * ✅ - * ✅ - * ⭕ -- * FastHunyuan - * `FastVideo/FastHunyuan-diffusers` - * 720px1280p
544px960p - * ❌ - * ✅ - * ✅ - * ⭕ -- * Wan2.1 T2V 1.3B - * `Wan-AI/Wan2.1-T2V-1.3B-Diffusers` - * 480P - * ✅ - * ✅* - * ✅ - * ⭕ -- * Wan2.1 T2V 14B - * `Wan-AI/Wan2.1-T2V-14B-Diffusers` - * 480P, 720P - * ✅ - * ✅* - * ✅ - * ⭕ -- * Wan2.1 I2V 480P - * `Wan-AI/Wan2.1-I2V-14B-480P-Diffusers` - * 480P - * ✅ - * ✅* - * ✅ - * ⭕ -- * Wan2.1 I2V 720P - * `Wan-AI/Wan2.1-I2V-14B-720P-Diffusers` - * 720P - * ✅ - * ✅ - * ✅ - * ⭕ -- * StepVideo T2V - * `FastVideo/stepvideo-t2v-diffusers` - * 768px768px204f
544px992px204f
544px992px136f - * ❌ - * ❌ - * ✅ - * ⭕ -::: - -**Note**: Wan2.2 TI2V 5B has some quality issues when performing I2V generation. We are working on fixing this issue. - -## Special requirements - -### StepVideo T2V -- The self-attention in text-encoder (step_llm) only supports CUDA capabilities sm_80 sm_86 and sm_90 - -### Sliding Tile Attention -- Currently only Hopper GPUs (H100s) are supported. diff --git a/docs/source/training/data_preprocess.md b/docs/training/data_preprocess.md similarity index 98% rename from docs/source/training/data_preprocess.md rename to docs/training/data_preprocess.md index ede86cbff..dd6c34990 100644 --- a/docs/source/training/data_preprocess.md +++ b/docs/training/data_preprocess.md @@ -1,4 +1,3 @@ -(v0-data-preprocess)= # 🧱 Data Preprocess diff --git a/docs/source/training/finetune.md b/docs/training/finetune.md similarity index 99% rename from docs/source/training/finetune.md rename to docs/training/finetune.md index 92636779c..390b20afd 100644 --- a/docs/source/training/finetune.md +++ b/docs/training/finetune.md @@ -1,4 +1,4 @@ -(v0-finetune)= + # 🧠 Finetune ## ⚡ Full Finetune Ensure your data is prepared and preprocessed in the format specified in [data_preprocess.md](#v0-data-preprocess). For convenience, we also provide a mochi preprocessed Black Myth Wukong data that can be downloaded directly: diff --git a/docs/source/video_sparse_attention/installation.md b/docs/video_sparse_attention/installation.md similarity index 98% rename from docs/source/video_sparse_attention/installation.md rename to docs/video_sparse_attention/installation.md index e436922b0..4da426b57 100644 --- a/docs/source/video_sparse_attention/installation.md +++ b/docs/video_sparse_attention/installation.md @@ -1,4 +1,3 @@ -(vsa-installation)= # 🔧 Installation You can install the Video Sparse Attention package using diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000..b93756ec8 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,169 @@ +site_name: FastVideo +site_description: A unified inference and post-training framework for accelerated video generation +site_author: FastVideo Team +site_url: http://127.0.0.1:8000/ + +# Documentation directory +docs_dir: docs + +# Repository +repo_name: hao-ai-lab/FastVideo +repo_url: https://github.com/hao-ai-lab/FastVideo +edit_uri: edit/main/docs/ + +# Configuration +theme: + name: material + palette: + - scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to light mode + features: + - navigation.tabs + - navigation.instant + - navigation.tracking + - search.highlight + - search.share + - content.tabs.link + - content.code.copy + - content.code.annotate + icon: + logo: material/book-open-variant + font: + text: Roboto + code: Roboto Mono + +# Plugins +plugins: + - search: + lang: en + - mkdocs-simple-hooks: + hooks: + on_pre_build: "docs.generate_examples:on_pre_build_hook" + - autorefs + - api-autonav: + modules: ["fastvideo"] + api_root_uri: "api" + exclude: + - "re:fastvideo\\._.*" + - mkdocstrings: + handlers: + python: + paths: [.] + options: + docstring_style: google + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_symbol_type_heading: true + show_object_full_path: true + show_category_heading: true + signature_crossrefs: true + show_if_no_docstring: false + show_signature_annotations: true + separate_signature: true + merge_init_into_class: true + show_submodules: true + show_bases: true + show_root_full_path: false + show_root_members_full_path: false + inventories: + - https://docs.python.org/3/objects.inv + + +# Markdown extensions +markdown_extensions: + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + - pymdownx.tabbed: + alternate_style: true + - pymdownx.emoji + - pymdownx.superfences + - pymdownx.arithmatex: + generic: true + - pymdownx.critic + - pymdownx.details + - pymdownx.highlight + - pymdownx.inlinehilite + - pymdownx.keys + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde + - toc: + permalink: true + +# Page tree +nav: + - Home: index.md + - Getting Started: + - Installation: getting_started/installation.md + - Quick Start: getting_started/quick_start.md + - V1 API: getting_started/v1_api.md + - Inference: + - Quick Start: inference/inference_quick_start.md + - Configuration: inference/configuration.md + - Optimizations: inference/optimizations.md + - ComfyUI: inference/comfyui.md + - Support Matrix: inference/support_matrix.md + - CLI: inference/cli.md + - Add Pipeline: inference/add_pipeline.md + - Examples: + - Basic: inference/examples/basic.md + - Optimizations: inference/examples/optimizations.md + - STA Mask Search: inference/examples/sta_mask_search.md + - Training: + - Data Preprocessing: training/data_preprocess.md + - Fine-tuning: training/finetune.md + - Examples: + - Training Index: training/examples/examples_training_index.md + - Wan T2V 1.3B: training/examples/wan_t2v_1.3B.md + - Wan I2V 14B 480p: training/examples/wan_i2v_14B_480p.md + - Wan2.1 Fun 1.3B InP: training/examples/Wan2.1-Fun-1.3B-InP.md + - Wan2.1 VSA: training/examples/Wan2.1-VSA.md + - Fine-tuning: training/examples/finetune.md + - Distillation: + - Data Preprocessing: distillation/data_preprocess.md + - DMD: distillation/dmd.md + - Sliding Tile Attention: + - Installation: sliding_tile_attention/installation.md + - Demo: sliding_tile_attention/demo.md + - Video Sparse Attention: + - Installation: video_sparse_attention/installation.md + - Design: + - Overview: design/overview.md + - Developer Guide: + - Overview: contributing/overview.md + - Developer Environment: + - Index: contributing/developer_env/index.md + - Docker: contributing/developer_env/docker.md + - RunPod: contributing/developer_env/runpod.md + - Profiling: contributing/profiling.md + - API Reference: + - FastVideo: api/fastvideo.md + + +# Extra +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/hao-ai-lab/FastVideo + version: + provider: mike + +# Custom CSS +extra_css: + - assets/custom.css \ No newline at end of file diff --git a/requirements-mkdocs.txt b/requirements-mkdocs.txt new file mode 100644 index 000000000..fa29dc8fe --- /dev/null +++ b/requirements-mkdocs.txt @@ -0,0 +1,13 @@ +mkdocs-simple-hooks>=0.1.5 +mkdocs-minify-plugin>=0.7.0 +mkdocs>=1.5.0 +mkdocs-material>=9.4.0 +mkdocstrings>=0.24.0 +mkdocstrings-python>=1.8.0 +mkdocs-mermaid2-plugin>=1.1.0 +mkdocs-git-revision-date-localized-plugin>=1.2.0 +mkdocs-git-committers-plugin-2>=1.1.0 +mkdocs-macros-plugin>=0.8.0 +pymdown-extensions>=10.0 +mkdocs-api-autonav +mkdocs-autorefs