From 8e8981a5c472d84041cf303935432ed3adf416ea Mon Sep 17 00:00:00 2001 From: "Mario Limonciello (AMD)" Date: Sat, 14 Feb 2026 06:46:18 -0600 Subject: [PATCH 1/3] Add TheRock ROCm tarball support for Stable Diffusion on Linux - Add TheRock configuration to backend_versions.json with version 7.11.0 and support for 13 architectures (gfx908, gfx90a, gfx942, gfx1030, gfx1031, gfx1032, gfx1100, gfx1101, gfx1102, gfx1151, gfx1150, gfx1200, gfx1201) - Implement system ROCm detection in backend_utils.cpp that checks LD_LIBRARY_PATH and standard system paths (/opt/rocm, /usr/lib*) for libamdhip64.so - Add TheRock download and installation logic that fetches architecture-specific tarballs from repo.amd.com/rocm/tarball/ and extracts to ~/.cache/lemonade/bin/therock/{arch}-{version}/ - Implement automatic cleanup of old TheRock versions to save disk space - Update SD ROCm artifact version from master-506-1f30df9 to master-505-e212912 and change filename format to include ROCm version suffix (-rocm-7.11.0) - Modify sd_server.cpp to prepend TheRock lib directory to LD_LIBRARY_PATH when system ROCm is not available, enabling stable diffusion to use downloaded ROCm libraries Tested successfully on Strix Point (Radeon 890M, gfx1150) where TheRock was automatically downloaded (2.3GB compressed, 13GB extracted) and SD-Turbo generated images in ~12 seconds using ROCm/HIP acceleration. --- .../include/lemon/backends/backend_utils.h | 15 ++ src/cpp/resources/backend_versions.json | 20 +- src/cpp/server/backends/backend_utils.cpp | 211 ++++++++++++++++++ src/cpp/server/backends/sd_server.cpp | 23 +- 4 files changed, 266 insertions(+), 3 deletions(-) diff --git a/src/cpp/include/lemon/backends/backend_utils.h b/src/cpp/include/lemon/backends/backend_utils.h index 82532c5c5..016099247 100644 --- a/src/cpp/include/lemon/backends/backend_utils.h +++ b/src/cpp/include/lemon/backends/backend_utils.h @@ -50,6 +50,21 @@ namespace lemon::backends { /** Get the latest version number for the given recipe/backend */ static std::string get_backend_version(const std::string& recipe, const std::string& backend); + + /** Check if ROCm libraries are installed system-wide (Linux only) */ + static bool is_rocm_installed_system_wide(); + + /** Get TheRock installation directory for a specific architecture and version */ + static std::string get_therock_install_dir(const std::string& arch, const std::string& version); + + /** Download and install TheRock ROCm tarball for the specified architecture (Linux only) */ + static void install_therock(const std::string& arch, const std::string& version); + + /** Clean up old TheRock versions, keeping only the specified version */ + static void cleanup_old_therock_versions(const std::string& current_version); + + /** Get TheRock lib directory path if available, or empty string if not needed */ + static std::string get_therock_lib_path(const std::string& rocm_arch); #endif /** Get the path to the backend's binary. Gives precedence to the path set through environment variables, if set. Throws if not found. */ diff --git a/src/cpp/resources/backend_versions.json b/src/cpp/resources/backend_versions.json index 433af0377..d1c34daf8 100644 --- a/src/cpp/resources/backend_versions.json +++ b/src/cpp/resources/backend_versions.json @@ -14,7 +14,25 @@ }, "sd-cpp": { "cpu": "master-506-1f30df9", - "rocm": "master-506-1f30df9" + "rocm": "master-505-e212912" + }, + "therock": { + "version": "7.11.0", + "architectures": [ + "gfx908", + "gfx90a", + "gfx942", + "gfx1030", + "gfx1031", + "gfx1032", + "gfx1100", + "gfx1101", + "gfx1102", + "gfx1151", + "gfx1150", + "gfx1200", + "gfx1201" + ] }, "ryzenai-server": "v1.7.0", "flm": { diff --git a/src/cpp/server/backends/backend_utils.cpp b/src/cpp/server/backends/backend_utils.cpp index 1a2e996f1..f0fd838b2 100644 --- a/src/cpp/server/backends/backend_utils.cpp +++ b/src/cpp/server/backends/backend_utils.cpp @@ -316,5 +316,216 @@ namespace lemon::backends { std::cout << "[" << spec.log_name() << "] Found executable at: " << exe_path << std::endl; } } + + bool BackendUtils::is_rocm_installed_system_wide() { +#ifndef __linux__ + // Only check on Linux + return false; +#else + // Check common ROCm library locations + std::vector search_paths; + + // Check LD_LIBRARY_PATH first + const char* ld_library_path = std::getenv("LD_LIBRARY_PATH"); + if (ld_library_path && strlen(ld_library_path) > 0) { + std::string path_str(ld_library_path); + size_t start = 0; + size_t end = path_str.find(':'); + while (end != std::string::npos) { + search_paths.push_back(path_str.substr(start, end - start)); + start = end + 1; + end = path_str.find(':', start); + } + search_paths.push_back(path_str.substr(start)); + } + + // Add standard system paths + search_paths.push_back("/opt/rocm/lib"); + search_paths.push_back("/opt/rocm/lib64"); + search_paths.push_back("/usr/lib"); + search_paths.push_back("/usr/lib64"); + search_paths.push_back("/usr/lib/x86_64-linux-gnu"); + + // Check for key ROCm library + for (const auto& path : search_paths) { + fs::path lib_path = fs::path(path) / "libamdhip64.so"; + if (fs::exists(lib_path)) { + std::cout << "[BackendUtils] Found system ROCm at: " << lib_path << std::endl; + return true; + } + } + + std::cout << "[BackendUtils] No system-wide ROCm installation detected" << std::endl; + return false; +#endif + } + + std::string BackendUtils::get_therock_install_dir(const std::string& arch, const std::string& version) { + fs::path therock_base = fs::path(utils::get_downloaded_bin_dir()) / "therock"; + return (therock_base / (arch + "-" + version)).string(); + } + + void BackendUtils::cleanup_old_therock_versions(const std::string& current_version) { +#ifdef __linux__ + fs::path therock_base = fs::path(utils::get_downloaded_bin_dir()) / "therock"; + + if (!fs::exists(therock_base)) { + return; // Nothing to clean up + } + + try { + for (const auto& entry : fs::directory_iterator(therock_base)) { + if (entry.is_directory()) { + std::string dir_name = entry.path().filename().string(); + // Directory format is "{arch}-{version}" + size_t dash_pos = dir_name.rfind('-'); + if (dash_pos != std::string::npos) { + std::string version = dir_name.substr(dash_pos + 1); + if (version != current_version) { + std::cout << "[BackendUtils] Cleaning up old TheRock version: " << dir_name << std::endl; + fs::remove_all(entry.path()); + } + } + } + } + } catch (const std::exception& e) { + std::cerr << "[BackendUtils] Warning: Failed to cleanup old TheRock versions: " << e.what() << std::endl; + } +#endif + } + + void BackendUtils::install_therock(const std::string& arch, const std::string& version) { +#ifndef __linux__ + throw std::runtime_error("TheRock is only supported on Linux"); +#else + std::string install_dir = get_therock_install_dir(arch, version); + std::string version_file = (fs::path(install_dir) / "version.txt").string(); + + // Check if already installed + if (fs::exists(install_dir) && fs::exists(version_file)) { + std::string installed_version; + std::ifstream vf(version_file); + std::getline(vf, installed_version); + vf.close(); + + if (installed_version == version) { + std::cout << "[BackendUtils] TheRock " << arch << "-" << version << " already installed" << std::endl; + return; + } + } + + std::cout << "[BackendUtils] Installing TheRock ROCm " << version << " for " << arch << std::endl; + + // Create install directory + fs::create_directories(install_dir); + + // Build URL + std::string filename = "therock-dist-linux-" + arch + "-" + version + ".tar.gz"; + std::string url = "https://repo.amd.com/rocm/tarball/" + filename; + + // Download to temp + fs::path cache_dir = fs::temp_directory_path(); + std::string tarball_path = (cache_dir / filename).string(); + + std::cout << "[BackendUtils] Downloading TheRock from: " << url << std::endl; + std::cout << "[BackendUtils] Downloading to: " << tarball_path << std::endl; + + auto download_result = utils::HttpClient::download_file( + url, + tarball_path, + utils::create_throttled_progress_callback() + ); + + if (!download_result.success) { + throw std::runtime_error("Failed to download TheRock from: " + url + + " - " + download_result.error_message); + } + + std::cout << std::endl << "[BackendUtils] Download complete!" << std::endl; + + // Verify download + if (!fs::exists(tarball_path)) { + throw std::runtime_error("Downloaded TheRock tarball does not exist: " + tarball_path); + } + + std::uintmax_t file_size = fs::file_size(tarball_path); + std::cout << "[BackendUtils] Downloaded tarball size: " + << (file_size / 1024 / 1024) << " MB" << std::endl; + + // Extract + if (!extract_tarball(tarball_path, install_dir, "TheRock")) { + fs::remove(tarball_path); + fs::remove_all(install_dir); + throw std::runtime_error("Failed to extract TheRock tarball: " + tarball_path); + } + + // Verify lib directory exists + fs::path lib_dir = fs::path(install_dir) / "lib"; + if (!fs::exists(lib_dir)) { + std::cerr << "[BackendUtils] ERROR: TheRock lib directory not found after extraction" << std::endl; + fs::remove(tarball_path); + fs::remove_all(install_dir); + throw std::runtime_error("TheRock extraction failed: lib directory not found"); + } + + std::cout << "[BackendUtils] TheRock lib directory verified at: " << lib_dir << std::endl; + + // Save version info + std::ofstream vf(version_file); + vf << version; + vf.close(); + + // Delete tarball + fs::remove(tarball_path); + + // Cleanup old versions + cleanup_old_therock_versions(version); + + std::cout << "[BackendUtils] TheRock installation complete!" << std::endl; +#endif + } + + std::string BackendUtils::get_therock_lib_path(const std::string& rocm_arch) { +#ifndef __linux__ + // TheRock only needed on Linux + return ""; +#else + // Check if system ROCm is available + if (is_rocm_installed_system_wide()) { + return ""; // Use system ROCm, no need for TheRock + } + + // Need TheRock - ensure it's installed + std::string config_path = utils::get_resource_path("resources/backend_versions.json"); + json config = utils::JsonUtils::load_from_file(config_path); + + if (!config.contains("therock") || !config["therock"].contains("version")) { + throw std::runtime_error("backend_versions.json is missing 'therock.version'"); + } + + std::string version = config["therock"]["version"].get(); + + // Validate architecture is supported + if (config["therock"].contains("architectures") && config["therock"]["architectures"].is_array()) { + bool arch_supported = false; + for (const auto& arch : config["therock"]["architectures"]) { + if (arch.is_string() && arch.get() == rocm_arch) { + arch_supported = true; + break; + } + } + if (!arch_supported) { + throw std::runtime_error("TheRock does not support architecture: " + rocm_arch); + } + } + + // Install if needed + install_therock(rocm_arch, version); + + // Return lib path + std::string install_dir = get_therock_install_dir(rocm_arch, version); + return (fs::path(install_dir) / "lib").string(); +#endif + } #endif } // namespace lemon::backends diff --git a/src/cpp/server/backends/sd_server.cpp b/src/cpp/server/backends/sd_server.cpp index 00ac47da6..8a20f3a51 100644 --- a/src/cpp/server/backends/sd_server.cpp +++ b/src/cpp/server/backends/sd_server.cpp @@ -58,7 +58,7 @@ void SDServer::install(const std::string& backend) { #ifdef _WIN32 filename = "sd-" + short_version + "-bin-win-rocm-x64.zip"; #elif defined(__linux__) - filename = "sd-" + short_version + "-bin-Linux-Ubuntu-24.04-x86_64-rocm.zip"; + filename = "sd-" + short_version + "-bin-Linux-Ubuntu-24.04-x86_64-rocm-7.11.0.zip"; #else throw std::runtime_error("ROCm sd.cpp only supported on Windows and Linux"); #endif @@ -147,9 +147,28 @@ void SDServer::load(const std::string& model_name, fs::path exe_dir = fs::path(exe_path).parent_path(); #ifndef _WIN32 - // For Linux, always set LD_LIBRARY_PATH to include executable directory + // For Linux, set LD_LIBRARY_PATH to include executable directory std::string lib_path = exe_dir.string(); + // For ROCm backend on Linux, also include TheRock if needed + if (backend == "rocm") { + std::string rocm_arch = lemon::SystemInfo::get_rocm_arch(); + if (!rocm_arch.empty()) { + try { + std::string therock_lib = BackendUtils::get_therock_lib_path(rocm_arch); + if (!therock_lib.empty()) { + // Prepend TheRock lib path + lib_path = therock_lib + ":" + lib_path; + std::cout << "[SDServer] Using TheRock ROCm libraries from: " << therock_lib << std::endl; + } else { + std::cout << "[SDServer] Using system-wide ROCm installation" << std::endl; + } + } catch (const std::exception& e) { + std::cerr << "[SDServer] Warning: Failed to setup TheRock: " << e.what() << std::endl; + } + } + } + const char* existing_ld_path = std::getenv("LD_LIBRARY_PATH"); if (existing_ld_path && strlen(existing_ld_path) > 0) { lib_path = lib_path + ":" + std::string(existing_ld_path); From 36c881a1911d402739ffa78be061c6bf0d2cda15 Mon Sep 17 00:00:00 2001 From: "Mario Limonciello (AMD)" Date: Sun, 15 Feb 2026 08:20:00 -0600 Subject: [PATCH 2/3] Adjust URL mappings --- src/cpp/resources/backend_versions.json | 17 +++++++++++++---- src/cpp/server/backends/backend_utils.cpp | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/cpp/resources/backend_versions.json b/src/cpp/resources/backend_versions.json index d1c34daf8..c6c591073 100644 --- a/src/cpp/resources/backend_versions.json +++ b/src/cpp/resources/backend_versions.json @@ -22,9 +22,6 @@ "gfx908", "gfx90a", "gfx942", - "gfx1030", - "gfx1031", - "gfx1032", "gfx1100", "gfx1101", "gfx1102", @@ -32,7 +29,19 @@ "gfx1150", "gfx1200", "gfx1201" - ] + ], + "url_mapping": { + "gfx908": "gfx90X-dcgpu", + "gfx90a": "gfx90X-dcgpu", + "gfx942": "gfx94X-dcgpu", + "gfx1100": "gfx110X-all", + "gfx1101": "gfx110X-all", + "gfx1102": "gfx110X-all", + "gfx1151": "gfx1151", + "gfx1150": "gfx1150", + "gfx1200": "gfx120X-all", + "gfx1201": "gfx120X-all" + } }, "ryzenai-server": "v1.7.0", "flm": { diff --git a/src/cpp/server/backends/backend_utils.cpp b/src/cpp/server/backends/backend_utils.cpp index f0fd838b2..c0a828c18 100644 --- a/src/cpp/server/backends/backend_utils.cpp +++ b/src/cpp/server/backends/backend_utils.cpp @@ -419,8 +419,18 @@ namespace lemon::backends { // Create install directory fs::create_directories(install_dir); - // Build URL - std::string filename = "therock-dist-linux-" + arch + "-" + version + ".tar.gz"; + // Get the URL variant for this architecture from config + std::string config_path = utils::get_resource_path("resources/backend_versions.json"); + json config = utils::JsonUtils::load_from_file(config_path); + + std::string url_variant = arch; // default to arch name + if (config.contains("therock") && config["therock"].contains("url_mapping") && + config["therock"]["url_mapping"].contains(arch)) { + url_variant = config["therock"]["url_mapping"][arch].get(); + } + + // Build URL with the correct variant + std::string filename = "therock-dist-linux-" + url_variant + "-" + version + ".tar.gz"; std::string url = "https://repo.amd.com/rocm/tarball/" + filename; // Download to temp From cc089df86f8a6517a905b56c081e21c26812fe86 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 19 Feb 2026 13:14:35 -0600 Subject: [PATCH 3/3] Update Windows artifacts as well --- src/cpp/resources/backend_versions.json | 4 ++-- src/cpp/server/backends/sd_server.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cpp/resources/backend_versions.json b/src/cpp/resources/backend_versions.json index c6c591073..c640268a5 100644 --- a/src/cpp/resources/backend_versions.json +++ b/src/cpp/resources/backend_versions.json @@ -13,8 +13,8 @@ "npu": "v1.8.2" }, "sd-cpp": { - "cpu": "master-506-1f30df9", - "rocm": "master-505-e212912" + "cpu": "master-509-846e9fe", + "rocm": "master-509-846e9fe" }, "therock": { "version": "7.11.0", diff --git a/src/cpp/server/backends/sd_server.cpp b/src/cpp/server/backends/sd_server.cpp index 8a20f3a51..25c6a5b44 100644 --- a/src/cpp/server/backends/sd_server.cpp +++ b/src/cpp/server/backends/sd_server.cpp @@ -30,7 +30,7 @@ SDServer::~SDServer() { } void SDServer::install(const std::string& backend) { - std::string repo = "superm1/stable-diffusion.cpp"; + std::string repo = "lemonade-sdk/stable-diffusion.cpp"; std::string filename; std::string expected_version = BackendUtils::get_backend_version(SPEC.recipe, backend); @@ -56,7 +56,7 @@ void SDServer::install(const std::string& backend) { } #ifdef _WIN32 - filename = "sd-" + short_version + "-bin-win-rocm-x64.zip"; + filename = "sd-" + short_version + "-bin-win-rocm-7.1.1-x64.zip"; #elif defined(__linux__) filename = "sd-" + short_version + "-bin-Linux-Ubuntu-24.04-x86_64-rocm-7.11.0.zip"; #else