| // Copyright 2023 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use std::{collections::HashSet, path::Path, sync::Arc}; |
| |
| use anyhow::{Context, Result}; |
| use log::debug; |
| use rand::Rng; |
| use tempfile::TempDir; |
| |
| use crate::{ |
| common::{ |
| steam_app_id_to_dlc, SteamAppId, CRYPTO_HOME, GPU_DEVICE_ID, IMAGE_LOADER, |
| PRECOMPILED_CACHE_DIR, |
| }, |
| dbus_wrapper::MockDbusConnectionTrait, |
| shader_cache_mount::{ShaderCacheMount, ShaderCacheMountMap, VmId}, |
| }; |
| |
| lazy_static! { |
| pub static ref MESA_VERSION_HASH: String = { |
| let random_bytes: u128 = rand::thread_rng().gen(); |
| format!("{:x}", random_bytes) |
| }; |
| } |
| |
| // TODO(endlesspring): organize the helper functions into modules or structs |
| // to improve readability. |
| |
| fn create_mock_mesa_shader_cache_sf(base_dir: &Path) -> Result<()> { |
| let mesa_path = base_dir |
| .join("mesa_shader_cache_sf") |
| .join(&*MESA_VERSION_HASH) |
| .join(format!("anv_{:04x}", *GPU_DEVICE_ID)); |
| std::fs::create_dir_all(&mesa_path).context("Failed to create mesa_shader_cache_sf")?; |
| std::fs::write(mesa_path.join("index"), "").context("Failed to create empty index file")?; |
| std::fs::write(mesa_path.join("foz_cache.foz"), "") |
| .context("Failed to create empty foz_cache.foz file")?; |
| std::fs::write(mesa_path.join("foz_cache_idx.foz"), "") |
| .context("Failed to create empty foz_cache_idx.foz file")?; |
| |
| debug!("Created mock mesa_shader_cache_sf at {:?}", mesa_path); |
| |
| Ok(()) |
| } |
| |
| pub fn generate_mount_list(mock_gpu_cache: &TempDir, steam_app_id: SteamAppId) -> String { |
| format!( |
| "/some/path on {} (some,options)\n", |
| mock_gpu_cache |
| .path() |
| .join("render_server") |
| .join("mesa_shader_cache_sf") |
| .join(&*MESA_VERSION_HASH) |
| .join(format!("anv_{:04x}", *GPU_DEVICE_ID)) |
| .join(steam_app_id.to_string()) |
| .to_str() |
| .unwrap() |
| ) |
| } |
| |
| pub fn mock_gpucache() -> Result<TempDir> { |
| let mock_gpu_cache = tempfile::tempdir()?; |
| let render_server_path = mock_gpu_cache.path().join("render_server"); |
| std::fs::create_dir_all(&render_server_path).context("Failed to create render_server")?; |
| std::fs::File::create(render_server_path.join("foz_db_list.txt"))?; |
| |
| create_mock_mesa_shader_cache_sf(&render_server_path)?; |
| |
| Ok(mock_gpu_cache) |
| } |
| |
| pub fn mock_shader_cache_dlc() -> Result<SteamAppId> { |
| let random_id: SteamAppId = rand::thread_rng().gen(); |
| debug!("Generating shader cache dlc for {:?}", random_id); |
| |
| let base_dir = IMAGE_LOADER |
| .join(steam_app_id_to_dlc(random_id)) |
| .join("package/root"); |
| create_mock_mesa_shader_cache_sf(&base_dir)?; |
| Ok(random_id) |
| } |
| |
| pub fn clean_up_mock_shader_cache_dlc(steam_app_id: SteamAppId) -> Result<()> { |
| std::fs::remove_dir_all(IMAGE_LOADER.join(steam_app_id_to_dlc(steam_app_id)))?; |
| Ok(()) |
| } |
| |
| pub fn foz_db_list_contains(mock_gpu_cache: &TempDir, game_id: SteamAppId) -> Result<bool> { |
| let foz_db_list_contents = |
| std::fs::read_to_string(mock_gpu_cache.path().join("render_server/foz_db_list.txt"))?; |
| debug!("Foz db list contents {}", foz_db_list_contents); |
| Ok(foz_db_list_contents |
| .trim() |
| .contains(&format!("{}/foz_cache", game_id))) |
| } |
| |
| pub fn foz_db_list_empty(mock_gpu_cache: &TempDir) -> Result<bool> { |
| let foz_db_list_contents = |
| std::fs::read_to_string(mock_gpu_cache.path().join("render_server/foz_db_list.txt"))?; |
| Ok(foz_db_list_contents.trim().is_empty()) |
| } |
| |
| pub async fn add_shader_cache_mount( |
| mock_gpu_cache: &TempDir, |
| mount_map: Arc<ShaderCacheMountMap>, |
| vm_id: &VmId, |
| ) -> Result<()> { |
| let mut mount_map_write = mount_map.write().await; |
| let mut shader_cache_mount = ShaderCacheMount::new(mock_gpu_cache.path().to_path_buf(), vm_id)?; |
| |
| let mut mock_conn = MockDbusConnectionTrait::new(); |
| mock_conn |
| .expect_call_dbus_method() |
| .times(1) |
| .returning(move |_, _, _, _, _: (Vec<u8>,)| Box::pin(async { Ok(()) })); |
| |
| shader_cache_mount |
| .initialize(vm_id, Arc::new(mock_conn)) |
| .await?; |
| mount_map_write.insert(vm_id.clone(), shader_cache_mount); |
| |
| Ok(()) |
| } |
| |
| pub async fn enqueue_mount( |
| mount_map: Arc<ShaderCacheMountMap>, |
| vm_id: &VmId, |
| steam_app_id: SteamAppId, |
| ) -> Result<()> { |
| let mut mount_map_write = mount_map.write().await; |
| let shader_cache_mount = mount_map_write.get_mut(vm_id).unwrap(); |
| shader_cache_mount.enqueue_mount(steam_app_id); |
| drop(mount_map_write); |
| Ok(()) |
| } |
| |
| pub async fn get_mount_queue( |
| mount_map: Arc<ShaderCacheMountMap>, |
| vm_id: &VmId, |
| ) -> Result<HashSet<SteamAppId>> { |
| let mount_map_read = mount_map.read().await; |
| let shader_cache_mount = mount_map_read.get(vm_id).unwrap(); |
| let cloned_queue = shader_cache_mount.get_mount_queue().clone(); |
| drop(mount_map_read); |
| Ok(cloned_queue) |
| } |
| |
| pub async fn get_unmount_queue( |
| mount_map: Arc<ShaderCacheMountMap>, |
| vm_id: &VmId, |
| ) -> Result<HashSet<SteamAppId>> { |
| let mount_map_read = mount_map.read().await; |
| let shader_cache_mount = mount_map_read.get(vm_id).unwrap(); |
| let cloned_queue = shader_cache_mount.get_unmount_queue().clone(); |
| drop(mount_map_read); |
| Ok(cloned_queue) |
| } |
| |
| pub async fn simulate_mounted(mock_gpu_cache: &TempDir, steam_app_id: SteamAppId) -> Result<()> { |
| let foz_db_list_file = mock_gpu_cache.path().join("render_server/foz_db_list.txt"); |
| let mut contents = std::fs::read_to_string(&foz_db_list_file)?; |
| contents += &format!("{}/foz_cache", steam_app_id); |
| contents += "\n"; |
| std::fs::write(foz_db_list_file, contents)?; |
| |
| let mesa_path = mock_gpu_cache |
| .path() |
| .join("render_server/mesa_shader_cache_sf") |
| .join(&*MESA_VERSION_HASH) |
| .join(format!("anv_{:04x}", *GPU_DEVICE_ID)); |
| std::fs::create_dir(mesa_path.join(steam_app_id.to_string()))?; |
| |
| Ok(()) |
| } |
| |
| pub async fn mount_destination_exists( |
| mock_gpu_cache: &TempDir, |
| steam_app_id: SteamAppId, |
| ) -> Result<bool> { |
| let foz_db_list_file = mock_gpu_cache.path().join("render_server/foz_db_list.txt"); |
| let mut contents = std::fs::read_to_string(&foz_db_list_file)?; |
| contents += &format!("{}/foz_cache", steam_app_id); |
| contents += "\n"; |
| std::fs::write(foz_db_list_file, contents)?; |
| |
| let mesa_path = mock_gpu_cache |
| .path() |
| .join("render_server/mesa_shader_cache_sf") |
| .join(&*MESA_VERSION_HASH) |
| .join(format!("anv_{:04x}", *GPU_DEVICE_ID)) |
| .join(steam_app_id.to_string()); |
| |
| Ok(mesa_path.exists()) |
| } |
| |
| pub fn populate_precompiled_cache(vm_ids: &[&VmId]) -> Result<()> { |
| let random_id: SteamAppId = rand::thread_rng().gen(); |
| // clean up cryptohome |
| for path in (std::fs::read_dir(&*CRYPTO_HOME)?).flatten() { |
| std::fs::remove_dir_all(path.path())?; |
| } |
| for vm_id in vm_ids { |
| let encoded_vm_name = base64::encode_config(&vm_id.vm_name, base64::URL_SAFE); |
| let base_path = CRYPTO_HOME |
| .join(&vm_id.vm_owner_id) |
| .join(PRECOMPILED_CACHE_DIR) |
| .join(encoded_vm_name) |
| .join(&random_id.to_string()); |
| std::fs::create_dir_all(base_path.join("fozpipelinesv6"))?; |
| std::fs::write( |
| base_path.join("fozpipelinesv6").join("replay-cache.foz"), |
| "", |
| )?; |
| std::fs::create_dir_all(base_path.join("DXVK_state_cache"))?; |
| std::fs::create_dir_all(base_path.join("fozmediaav1"))?; |
| } |
| Ok(()) |
| } |