Commit 133087d for stable-diffusion.cpp

commit 133087deb82abd8bcd4afdf0b895cf63a8714293
Author: leejet <leejet714@gmail.com>
Date:   Mon Nov 2 01:09:36 2026 +0800

    fix: prefer discrete GPUs in auto-fit device selection (#2259)

diff --git a/docs/backend.md b/docs/backend.md
index 49ca063..da90e48 100644
--- a/docs/backend.md
+++ b/docs/backend.md
@@ -135,7 +135,10 @@ regardless of argument order, even with `--auto-fit on`.

 Auto-fit preserves explicit `--backend` assignments, including per-module
 assignments and device lists. For modules without a runtime assignment, it chooses
-the GPU with the largest available memory budget (the first device on a tie).
+the discrete GPU with the largest positive memory budget (the first device on a
+tie), falling back to integrated GPUs when no discrete GPU has a positive budget.
+An integrated GPU's shared system memory does not give it priority over a discrete
+GPU. Explicit assignments such as `--backend Vulkan1` still select that device.
 It then derives parameter placements from the model metadata, each module's
 compute devices, and the remaining memory budgets. The chosen backend
 specifications are printed.
diff --git a/src/core/backend_fit.cpp b/src/core/backend_fit.cpp
index c970d10..120cbe4 100644
--- a/src/core/backend_fit.cpp
+++ b/src/core/backend_fit.cpp
@@ -42,8 +42,9 @@ namespace sd::backend_fit {
     struct Device {
         std::string name;
         std::string description;
-        int64_t free_bytes   = 0;
-        int64_t budget_bytes = 0;
+        int64_t free_bytes              = 0;
+        int64_t budget_bytes            = 0;
+        enum ggml_backend_dev_type type = GGML_BACKEND_DEVICE_TYPE_GPU;
     };

     enum class ParamsLocation {
@@ -140,6 +141,7 @@ namespace sd::backend_fit {
             Device device;
             device.name        = ggml_backend_dev_name(dev);
             device.description = ggml_backend_dev_description(dev);
+            device.type        = type;
             size_t free_bytes = 0, total_bytes = 0;
             ggml_backend_dev_memory(dev, &free_bytes, &total_bytes);
             device.free_bytes = (int64_t)free_bytes;
@@ -193,14 +195,23 @@ namespace sd::backend_fit {
     }

     static size_t select_main_device(const std::vector<Device>& devices) {
-        size_t main_device = SIZE_MAX;
-        for (size_t di = 0; di < devices.size(); ++di) {
-            if (devices[di].budget_bytes > 0 &&
-                (main_device == SIZE_MAX || devices[di].budget_bytes > devices[main_device].budget_bytes)) {
-                main_device = di;
+        // Shared system memory must not make an integrated GPU outrank a discrete GPU.
+        for (bool discrete : {true, false}) {
+            size_t main_device = SIZE_MAX;
+            for (size_t di = 0; di < devices.size(); ++di) {
+                if ((devices[di].type == GGML_BACKEND_DEVICE_TYPE_GPU) != discrete) {
+                    continue;
+                }
+                if (devices[di].budget_bytes > 0 &&
+                    (main_device == SIZE_MAX || devices[di].budget_bytes > devices[main_device].budget_bytes)) {
+                    main_device = di;
+                }
+            }
+            if (main_device != SIZE_MAX) {
+                return main_device;
             }
         }
-        return main_device;
+        return SIZE_MAX;
     }

     static Plan compute_plan(const std::vector<Component>& components,