rusty_stack/installers/components/
fastvideo.rs1use std::path::PathBuf;
16
17const FASTVIDEO_REPO: &str = "https://github.com/scooter-lacroix/FastVideo.git";
18const FASTVIDEO_BRANCH: &str = "feature/rocm-gfx11-support";
19const FASTVIDEO_BUILD_DIR: &str = "/tmp/FastVideo_ROCm_build";
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct ShellCommand {
24 pub program: String,
26 pub args: Vec<String>,
28 pub env: Vec<(String, String)>,
30 pub working_dir: Option<PathBuf>,
32}
33
34#[derive(Debug, Clone)]
36pub struct FastVideoConfig {
37 pub gpu_arch: String,
39 pub python_bin: String,
41}
42
43impl Default for FastVideoConfig {
44 fn default() -> Self {
45 Self {
46 gpu_arch: "gfx1100".to_string(),
47 python_bin: "python3".to_string(),
48 }
49 }
50}
51
52#[derive(Debug, Clone)]
54pub struct FastVideoInstaller {
55 repo_url: String,
56 branch: String,
57 build_dir: String,
58 config: FastVideoConfig,
59}
60
61impl Default for FastVideoInstaller {
62 fn default() -> Self {
63 Self::new(FastVideoConfig::default())
64 }
65}
66
67impl FastVideoInstaller {
68 pub fn new(config: FastVideoConfig) -> Self {
69 Self {
70 repo_url: FASTVIDEO_REPO.to_string(),
71 branch: FASTVIDEO_BRANCH.to_string(),
72 build_dir: FASTVIDEO_BUILD_DIR.to_string(),
73 config,
74 }
75 }
76
77 pub fn build_commands(&self) -> Vec<ShellCommand> {
79 vec![
80 self.mkdir_build_dir(),
81 self.git_clone(),
82 self.git_checkout(),
83 self.git_submodule_init(),
84 self.patch_cmake(),
85 self.install_build_deps(),
86 self.pip_install_kernel(),
87 self.cleanup(),
88 ]
89 }
90
91 pub fn mkdir_build_dir(&self) -> ShellCommand {
92 ShellCommand {
93 program: "mkdir".to_string(),
94 args: vec!["-p".to_string(), self.build_dir.clone()],
95 env: vec![],
96 working_dir: None,
97 }
98 }
99
100 pub fn git_clone(&self) -> ShellCommand {
101 ShellCommand {
102 program: "git".to_string(),
103 args: vec!["clone".to_string(), self.repo_url.clone(), ".".to_string()],
104 env: vec![],
105 working_dir: None,
106 }
107 }
108
109 pub fn git_checkout(&self) -> ShellCommand {
110 ShellCommand {
111 program: "git".to_string(),
112 args: vec!["checkout".to_string(), self.branch.clone()],
113 env: vec![],
114 working_dir: None,
115 }
116 }
117
118 pub fn git_submodule_init(&self) -> ShellCommand {
120 ShellCommand {
121 program: "git".to_string(),
122 args: vec![
123 "submodule".to_string(),
124 "update".to_string(),
125 "--init".to_string(),
126 "--recursive".to_string(),
127 ],
128 env: vec![],
129 working_dir: None,
130 }
131 }
132
133 pub fn patch_cmake(&self) -> ShellCommand {
152 ShellCommand {
153 program: "bash".to_string(),
154 args: vec![
155 "-c".to_string(),
156 "sed -i '/flash_attn_rocm\\.cpp/d' CMakeLists.txt && \
157 sed -i '4,23d' csrc/common_extension.cpp && \
158 sed -i '/Flash Attention ROCm$/d;/flash_attn_varlen_fwd/d;/flash_attn_varlen_bwd/d' csrc/common_extension.cpp".to_string(),
159 ],
160 env: vec![],
161 working_dir: None,
162 }
163 }
164
165 pub fn install_build_deps(&self) -> ShellCommand {
170 let use_break =
171 std::env::var("VIRTUAL_ENV").is_err() && std::env::var("CONDA_PREFIX").is_err();
172 let mut args = vec!["-m".to_string(), "pip".to_string(), "install".to_string()];
173 if use_break {
174 args.push("--break-system-packages".to_string());
175 }
176 args.extend([
177 "--upgrade".to_string(),
178 "--no-cache-dir".to_string(),
179 "scikit-build-core".to_string(),
180 "cmake".to_string(),
181 "ninja".to_string(),
182 ]);
183
184 ShellCommand {
185 program: self.config.python_bin.clone(),
186 args,
187 env: vec![],
188 working_dir: None,
189 }
190 }
191
192 pub fn pip_install_kernel(&self) -> ShellCommand {
201 let use_break =
202 std::env::var("VIRTUAL_ENV").is_err() && std::env::var("CONDA_PREFIX").is_err();
203 let mut args = vec!["-m".to_string(), "pip".to_string(), "install".to_string()];
204 if use_break {
205 args.push("--break-system-packages".to_string());
206 }
207 args.extend([
208 "-v".to_string(),
209 "--no-build-isolation".to_string(),
210 ".".to_string(),
211 ]);
212
213 let cmake_args = format!(
214 "-DCMAKE_HIP_ARCHITECTURES={} -DFASTVIDEO_KERNEL_BUILD_TK=OFF -DGPU_BACKEND=ROCM",
215 self.config.gpu_arch
216 );
217
218 let nproc = std::thread::available_parallelism()
219 .map(|n| n.get().saturating_sub(1))
220 .unwrap_or(4);
221
222 let env = vec![
223 ("CMAKE_ARGS".to_string(), cmake_args),
224 ("GPU_ARCHS".to_string(), self.config.gpu_arch.clone()),
225 ("MAX_JOBS".to_string(), nproc.to_string()),
226 ("GPU_BACKEND".to_string(), "ROCM".to_string()),
227 ];
228
229 ShellCommand {
230 program: self.config.python_bin.clone(),
231 args,
232 env,
233 working_dir: None,
234 }
235 }
236
237 pub fn cleanup(&self) -> ShellCommand {
238 ShellCommand {
239 program: "rm".to_string(),
240 args: vec!["-rf".to_string(), self.build_dir.clone()],
241 env: vec![],
242 working_dir: None,
243 }
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 #[test]
252 fn test_build_commands_count() {
253 let inst = FastVideoInstaller::new(FastVideoConfig::default());
254 let cmds = inst.build_commands();
255 assert_eq!(cmds.len(), 8, "Should produce 8 commands");
256 }
257
258 #[test]
259 fn test_git_clone_uses_fork() {
260 let inst = FastVideoInstaller::new(FastVideoConfig::default());
261 let cmds = inst.build_commands();
262 let clone_cmd = &cmds[1];
263 assert!(clone_cmd.args.contains(&"clone".into()));
264 assert!(
265 clone_cmd.args.contains(&FASTVIDEO_REPO.into()),
266 "Should clone from scooter-lacroix fork"
267 );
268 }
269
270 #[test]
271 fn test_submodule_init_present() {
272 let inst = FastVideoInstaller::new(FastVideoConfig::default());
273 let cmds = inst.build_commands();
274 let submod_cmd = &cmds[3];
275 assert!(submod_cmd.args.contains(&"submodule".into()));
276 assert!(submod_cmd.args.contains(&"--recursive".into()));
277 }
278
279 #[test]
280 fn test_pip_install_has_rocm_cmake_args() {
281 let inst = FastVideoInstaller::new(FastVideoConfig {
282 gpu_arch: "gfx1100".to_string(),
283 python_bin: "python3".to_string(),
284 });
285 let cmd = inst.pip_install_kernel();
286 assert!(cmd.args.contains(&"--no-build-isolation".into()));
287 let cmake_env = cmd.env.iter().find(|(k, _)| k == "CMAKE_ARGS").unwrap();
288 assert!(cmake_env.1.contains("CMAKE_HIP_ARCHITECTURES=gfx1100"));
289 assert!(cmake_env.1.contains("GPU_BACKEND=ROCM"));
290 assert!(cmake_env.1.contains("FASTVIDEO_KERNEL_BUILD_TK=OFF"));
291 }
292
293 #[test]
294 fn test_pip_install_sets_gpu_archs() {
295 let inst = FastVideoInstaller::new(FastVideoConfig {
296 gpu_arch: "gfx1200".to_string(),
297 python_bin: "python3".to_string(),
298 });
299 let cmd = inst.pip_install_kernel();
300 let gpu_env = cmd.env.iter().find(|(k, _)| k == "GPU_ARCHS").unwrap();
301 assert_eq!(gpu_env.1, "gfx1200");
302 }
303
304 #[test]
305 fn test_cleanup_removes_build_dir() {
306 let inst = FastVideoInstaller::new(FastVideoConfig::default());
307 let cmds = inst.build_commands();
308 let cleanup = &cmds[7];
309 assert!(cleanup.args.contains(&"-rf".into()));
310 assert!(cleanup.args.contains(&FASTVIDEO_BUILD_DIR.into()));
311 }
312
313 #[test]
314 fn test_config_propagates_python_bin() {
315 let inst = FastVideoInstaller::new(FastVideoConfig {
316 gpu_arch: "gfx1100".to_string(),
317 python_bin: "python3.12".to_string(),
318 });
319 let cmd = inst.pip_install_kernel();
320 assert_eq!(cmd.program, "python3.12");
321 }
322}