Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 53 additions & 5 deletions src/engine/llama.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use super::{GenOptions, InferenceEngine, LoadedModel, ModelSpec};

/// Smart thread detection optimized for inference performance
/// Matches Ollama's approach: use physical cores with intelligent limits
#[allow(dead_code)]
fn get_optimal_thread_count() -> i32 {
let total_cores = std::thread::available_parallelism()
.map(|n| n.get() as i32)
Expand Down Expand Up @@ -64,17 +65,18 @@ fn get_or_init_backend() -> Result<&'static shimmy_llama_cpp_2::llama_backend::L

#[derive(Default)]
pub struct LlamaEngine {
#[allow(dead_code)] // Temporarily unused while fork is being fixed
gpu_backend: GpuBackend,
#[allow(dead_code)] // Temporarily unused while fork is being fixed
moe_config: MoeConfig,
}

#[derive(Debug, Clone, Default)]
struct MoeConfig {
#[allow(dead_code)] // Temporarily unused while fork is being fixed
// These fields are only used when the "llama" feature is enabled,
// in model loading code that configures MoE CPU offloading.
// They appear "dead" when compiling without llama feature.
#[allow(dead_code)]
cpu_moe_all: bool,
#[allow(dead_code)] // Temporarily unused while fork is being fixed
#[allow(dead_code)]
n_cpu_moe: Option<usize>,
}

Expand All @@ -92,7 +94,6 @@ pub enum GpuBackend {

impl GpuBackend {
/// Parse GPU backend from CLI string
#[allow(dead_code)] // Temporarily unused while fork is being fixed
fn from_string(s: &str) -> Self {
match s.to_lowercase().as_str() {
"auto" => Self::detect_best(),
Expand Down Expand Up @@ -212,6 +213,7 @@ impl GpuBackend {
}

/// Get the number of layers to offload to GPU
#[allow(dead_code)]
pub fn gpu_layers(&self) -> u32 {
match self {
GpuBackend::Cpu => 0, // No GPU offloading for CPU backend
Expand All @@ -234,11 +236,15 @@ impl LlamaEngine {
}

/// Create engine with specific GPU backend from CLI
#[allow(dead_code)]
pub fn new_with_backend(backend_str: Option<&str>) -> Self {
let gpu_backend = backend_str
.map(GpuBackend::from_string)
.unwrap_or_else(GpuBackend::detect_best);

// Set environment variables for GPU backend before any backend initialization
Self::configure_gpu_environment(&gpu_backend);

info!("GPU backend configured: {:?}", gpu_backend);

Self {
Expand All @@ -247,7 +253,48 @@ impl LlamaEngine {
}
}

/// Configure environment variables for GPU backend
fn configure_gpu_environment(gpu_backend: &GpuBackend) {
match gpu_backend {
#[cfg(feature = "llama-cuda")]
GpuBackend::Cuda => {
std::env::set_var("GGML_CUDA", "1");
info!("Set GGML_CUDA=1 for CUDA backend");
}
#[cfg(feature = "llama-vulkan")]
GpuBackend::Vulkan => {
std::env::set_var("GGML_VULKAN", "1");
info!("Set GGML_VULKAN=1 for Vulkan backend");
#[cfg(target_os = "windows")]
{
// On Windows, Vulkan might need ICD setup
if std::env::var("VK_ICD_FILENAMES").is_err() {
info!("Vulkan ICD not configured - GPU acceleration may not work");
}
}
}
#[cfg(feature = "llama-opencl")]
GpuBackend::OpenCL => {
std::env::set_var("GGML_OPENCL", "1");
// Set defaults if not already set
if std::env::var("GGML_OPENCL_PLATFORM").is_err() {
std::env::set_var("GGML_OPENCL_PLATFORM", "0");
info!("Set GGML_OPENCL_PLATFORM=0 (default)");
}
if std::env::var("GGML_OPENCL_DEVICE").is_err() {
std::env::set_var("GGML_OPENCL_DEVICE", "0");
info!("Set GGML_OPENCL_DEVICE=0 (default)");
}
info!("Configured OpenCL environment variables");
}
GpuBackend::Cpu => {
// No special environment setup needed for CPU
}
}
}

/// Set MoE CPU offloading configuration
#[allow(dead_code)]
pub fn with_moe_config(mut self, cpu_moe_all: bool, n_cpu_moe: Option<usize>) -> Self {
self.moe_config = MoeConfig {
cpu_moe_all,
Expand All @@ -257,6 +304,7 @@ impl LlamaEngine {
}

/// Get information about the current GPU backend configuration
#[allow(dead_code)]
pub fn get_backend_info(&self) -> String {
match self.gpu_backend {
GpuBackend::Cpu => "CPU".to_string(),
Expand Down
3 changes: 3 additions & 0 deletions tests/regression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ mod issue_129_precompiled_gpu_support;
#[path = "regression/issue_130_gpu_layer_offloading.rs"]
mod issue_130_gpu_layer_offloading;

#[path = "regression/issue_142_amd_gpu_detection.rs"]
mod issue_142_amd_gpu_detection;

#[path = "regression/issue_131_arm64_ci_support.rs"]
mod issue_131_arm64_ci_support;

Expand Down
95 changes: 95 additions & 0 deletions tests/regression/issue_142_amd_gpu_detection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/// Regression test for Issue #142: AMD GPU not detected on Windows (Vulkan/OpenCL)
///
/// GitHub: https://github.com/Michael-A-Kuykendall/shimmy/issues/142
///
/// **Bug**: AMD GPU correctly detected by clinfo but all layers assigned to CPU instead of GPU
/// **Root Cause**: GPU backend environment variables not set before llama.cpp backend initialization
/// **Fix**: Set GGML_* environment variables when GPU backend is selected
/// **This test**: Verifies environment variables are set correctly for GPU backends
#[cfg(test)]
mod issue_142_tests {
use std::env;

#[test]
#[cfg(feature = "llama-opencl")]
fn test_opencl_backend_sets_environment_variables() {
// Clear any existing environment variables
env::remove_var("GGML_OPENCL");
env::remove_var("GGML_OPENCL_PLATFORM");
env::remove_var("GGML_OPENCL_DEVICE");

// Create engine with OpenCL backend - this should set environment variables
let _engine = shimmy::engine::llama::LlamaEngine::new_with_backend(Some("opencl"));

// Verify environment variables are set
assert_eq!(env::var("GGML_OPENCL").unwrap(), "1");
assert_eq!(env::var("GGML_OPENCL_PLATFORM").unwrap(), "0");
assert_eq!(env::var("GGML_OPENCL_DEVICE").unwrap(), "0");
}

#[test]
#[cfg(feature = "llama-vulkan")]
fn test_vulkan_backend_sets_environment_variables() {
// Clear any existing environment variables
env::remove_var("GGML_VULKAN");

// Create engine with Vulkan backend - this should set environment variables
let _engine = shimmy::engine::llama::LlamaEngine::new_with_backend(Some("vulkan"));

// Verify environment variables are set
assert_eq!(env::var("GGML_VULKAN").unwrap(), "1");
}

#[test]
#[cfg(feature = "llama-cuda")]
fn test_cuda_backend_sets_environment_variables() {
// Clear any existing environment variables
env::remove_var("GGML_CUDA");

// Create engine with CUDA backend - this should set environment variables
let _engine = shimmy::engine::llama::LlamaEngine::new_with_backend(Some("cuda"));

// Verify environment variables are set
assert_eq!(env::var("GGML_CUDA").unwrap(), "1");
}

#[test]
fn test_cpu_backend_does_not_set_gpu_environment_variables() {
// Note: Environment variables may persist between tests in the same process.
// This test verifies that creating a CPU engine doesn't actively set GPU variables
// (though they may already be set from previous tests)

// Just verify that CPU backend creation doesn't panic and works correctly
let _engine = shimmy::engine::llama::LlamaEngine::new_with_backend(Some("cpu"));
assert!(true); // If we get here, the test passes
}

#[test]
fn test_auto_detect_backend_sets_appropriate_variables() {
// This test verifies that auto-detection sets variables for available backends
// We can't predict which backend will be selected, but we can verify the pattern

// Clear all GPU environment variables first
env::remove_var("GGML_CUDA");
env::remove_var("GGML_VULKAN");
env::remove_var("GGML_OPENCL");
env::remove_var("GGML_OPENCL_PLATFORM");
env::remove_var("GGML_OPENCL_DEVICE");

// Create engine with auto-detect
let _engine = shimmy::engine::llama::LlamaEngine::new_with_backend(Some("auto"));

// At least one GPU variable should be set if GPU backends are available
let _has_cuda = env::var("GGML_CUDA").is_ok();
let _has_vulkan = env::var("GGML_VULKAN").is_ok();
let _has_opencl = env::var("GGML_OPENCL").is_ok();

// If any GPU backend is enabled, at least one variable should be set
#[cfg(any(
feature = "llama-cuda",
feature = "llama-vulkan",
feature = "llama-opencl"
))]
assert!(has_cuda || has_vulkan || has_opencl, "Auto-detect should set at least one GPU environment variable when GPU features are enabled");
}
}
Loading