Add support for DXVK state cache updates

This commit is contained in:
Mathieu Comandon 2022-10-24 02:35:48 -07:00
parent 5b80af94f5
commit 7d87054b22
4 changed files with 87 additions and 1 deletions

View file

@ -183,6 +183,17 @@ def get_game_installers(game_slug, revision=None):
return [normalize_installer(i) for i in installers]
def get_game_details(slug):
url = settings.SITE_URL + "/api/games/%s" % slug
request = http.Request(url)
try:
response = request.get()
except http.HTTPError as ex:
logger.debug("Unable to load %s: %s", slug, ex)
return {}
return response.json
def normalize_installer(installer):
"""Adjusts an installer dict so it is in the correct form, with values
of the expected types."""

View file

@ -125,6 +125,12 @@ class Game(GObject.Object):
value += " (%s)" % self.runner_name
return value
@property
def is_cache_managed(self):
"""Is the DXVK cache receiving updates from lutris?"""
env = self.runner.system_config.get("env", {})
return "DXVK_STATE_CACHE_PATH" in env
@property
def is_updatable(self):
"""Return whether the game can be upgraded"""

View file

@ -23,6 +23,7 @@ from lutris.util.log import logger
from lutris.util.steam import shortcut as steam_shortcut
from lutris.util.strings import gtk_safe
from lutris.util.system import path_exists
from lutris.util.wine.dxvk import update_shader_cache
class GameActions:
@ -64,11 +65,11 @@ class GameActions:
("install_dlcs", "Install DLCs", self.on_install_dlc_clicked),
("show_logs", _("Show logs"), self.on_show_logs),
("add", _("Add installed game"), self.on_add_manually),
("duplicate", _("Duplicate"), self.on_game_duplicate),
("configure", _("Configure"), self.on_edit_game_configuration),
("favorite", _("Add to favorites"), self.on_add_favorite_game),
("deletefavorite", _("Remove from favorites"), self.on_delete_favorite_game),
("execute-script", _("Execute script"), self.on_execute_script_clicked),
("update-shader-cache", _("Update shader cache"), self.on_update_shader_cache),
("browse", _("Browse files"), self.on_browse_files),
(
"desktop-shortcut",
@ -102,6 +103,7 @@ class GameActions:
),
("install_more", _("Install another version"), self.on_install_clicked),
("remove", _("Remove"), self.on_remove_game),
("duplicate", _("Duplicate"), self.on_game_duplicate),
("view", _("View on Lutris.net"), self.on_view_game),
("hide", _("Hide game from library"), self.on_hide_game),
("unhide", _("Unhide game from library"), self.on_unhide_game),
@ -115,6 +117,7 @@ class GameActions:
"install": not self.game.is_installed,
"play": self.game.is_installed and not self.is_game_running,
"update": self.game.is_updatable,
"update-shader-cache": self.game.is_cache_managed,
"install_dlcs": self.game.is_updatable,
"stop": self.is_game_running,
"configure": bool(self.game.is_installed),
@ -200,6 +203,9 @@ class GameActions:
def on_install_dlc_clicked(self, _widget):
self.game.emit("game-install-dlc")
def on_update_shader_cache(self, _widget):
update_shader_cache(self.game)
def on_locate_installed_game(self, _button, game):
"""Show the user a dialog to import an existing install to a DRM free service

View file

@ -1,7 +1,13 @@
"""DXVK helper module"""
import os
import shutil
from lutris import api
from lutris.settings import RUNTIME_DIR
from lutris.util.extract import extract_archive
from lutris.util.http import download_file
from lutris.util.log import logger
from lutris.util.system import create_folder, execute, remove_folder
from lutris.util.wine.dll_manager import DLLManager
@ -34,3 +40,60 @@ class DXVKManager(DLLManager):
except OSError:
pass
return False
def update_shader_cache(game):
state_cache_path = game.config.system_config["env"]["DXVK_STATE_CACHE_PATH"]
if not os.path.exists(state_cache_path):
logger.warning("%s is not a valid path", state_cache_path)
return False
game_details = api.get_game_details(game.slug)
if not game_details.get("shaders"):
logger.debug("No shaders for %s", game)
return False
last_updated_local = game.config.game_config.get("dxvk_cache_updated_at")
most_recent_update = None
shader_url = None
for shader in game_details["shaders"]:
if not most_recent_update or most_recent_update < shader["updated_at"]:
shader_url = shader["url"]
most_recent_update = shader["updated_at"]
if last_updated_local and last_updated_local >= most_recent_update:
logger.debug("Cache up to date")
return False
shader_merge_path = os.path.join(state_cache_path, "dxvk-state-cache")
create_folder(shader_merge_path)
shader_archive_path = os.path.join(shader_merge_path, os.path.basename(shader_url))
download_file(shader_url, shader_archive_path)
extract_archive(shader_archive_path, to_directory=shader_merge_path)
try:
remote_cache_path = [
shader_file for shader_file in os.listdir(shader_merge_path)
if shader_file.endswith(".dxvk-cache")
][0]
except IndexError:
logger.error("Cache path not found")
return False
cache_file_name = os.path.basename(remote_cache_path)
local_cache_path = os.path.join(state_cache_path, cache_file_name)
if not os.path.exists(local_cache_path):
shutil.copy(remote_cache_path, state_cache_path)
else:
local_copy_path = os.path.join(shader_merge_path, "Local.dxvk-cache")
output_path = os.path.join(shader_merge_path, "output.dxvk-cache")
shutil.copy(local_cache_path, local_copy_path)
state_merge_tool_path = os.path.join(RUNTIME_DIR, "dxvk-cache-tool/dxvk_cache_tool")
if not os.path.exists(state_merge_tool_path):
logger.error("dxvk_cache_tool not present")
return False
execute([
state_merge_tool_path,
remote_cache_path,
local_copy_path
], cwd=shader_merge_path)
if not os.path.exists(output_path):
logger.error("Merging of shader failed")
shutil.copy(output_path, local_cache_path)
remove_folder(shader_merge_path)
game.config.game_level["game"]["dxvk_cache_updated_at"] = most_recent_update
game.config.save()