File size: 5,283 Bytes
96db1c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bd7c6d9..a4938ae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -417,3 +417,9 @@ configure_file(cmake/stable-diffusion.pc.in
 
 install(FILES "${CMAKE_CURRENT_BINARY_DIR}/stable-diffusion.pc"
         DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+
+# ROCmFPX: build against the ROCmFPX fork of ggml (adds ROCmFP4/ROCmFPX quant types)
+option(SD_ROCMFPX "sd: ggml is the ROCmFPX fork; force-include API shims" OFF)
+if(SD_ROCMFPX)
+    target_compile_options(stable-diffusion PRIVATE -include ${CMAKE_CURRENT_SOURCE_DIR}/rfx_compat.h)
+endif()
diff --git a/include/stable-diffusion.h b/include/stable-diffusion.h
index 267e7a7..ace983e 100644
--- a/include/stable-diffusion.h
+++ b/include/stable-diffusion.h
@@ -144,7 +144,15 @@ enum sd_type_t {
     SD_TYPE_Q2_0    = 42,
     SD_TYPE_F8_E4M3 = 43,
     SD_TYPE_F8_E5M2 = 44,
-    SD_TYPE_COUNT   = 45,
+    // ROCmFPX fork of ggml (github.com/charlie12345/ROCmFPX); ids match its ggml_type
+    SD_TYPE_Q4_0_ROCMFP4      = 100,
+    SD_TYPE_Q4_0_ROCMFP4_FAST = 101,
+    SD_TYPE_Q6_0_ROCMFPX      = 102,
+    SD_TYPE_Q8_0_ROCMFPX      = 103,
+    SD_TYPE_Q3_0_ROCMFPX      = 104,
+    SD_TYPE_Q2_0_ROCMFPX      = 107,
+    SD_TYPE_Q4_0_ROCMI4       = 108,
+    SD_TYPE_COUNT             = 109,
 };
 
 enum sd_log_level_t {
diff --git a/src/model_io/gguf_io.cpp b/src/model_io/gguf_io.cpp
index 8120131..1337dbb 100644
--- a/src/model_io/gguf_io.cpp
+++ b/src/model_io/gguf_io.cpp
@@ -167,6 +167,17 @@ bool GGUFStreamingWriter::write_metadata(const std::string& file_path,
         return false;
     }
 
+    const bool is_moss_image = std::any_of(tensors.begin(), tensors.end(), [](const TensorWritePlan& tensor) {
+        return tensor.name == "video_embedder.bias";
+    }) && std::any_of(tensors.begin(), tensors.end(), [](const TensorWritePlan& tensor) {
+        return tensor.name == "layers.0.proj.q.weight";
+    }) && std::any_of(tensors.begin(), tensors.end(), [](const TensorWritePlan& tensor) {
+        return tensor.name == "final_linear_video.weight";
+    });
+    if (is_moss_image) {
+        gguf_set_val_str(gguf_ctx_, "general.architecture", "moss_image");
+    }
+
     for (const TensorWritePlan& plan : tensors) {
         ggml_tensor* tensor = ggml_new_tensor(meta_ctx_, plan.type, plan.n_dims, plan.ne);
         if (tensor == nullptr) {
diff --git a/src/model_loader.cpp b/src/model_loader.cpp
index 8822b2c..a677baa 100644
--- a/src/model_loader.cpp
+++ b/src/model_loader.cpp
@@ -1605,6 +1605,10 @@ bool ModelLoader::tensor_should_be_converted(const TensorStorage& tensor_storage
     if (tensor_storage.is_int8_tensorwise) {
         return false;
     }
+    if (type == GGML_TYPE_F32 && tensor_storage.type != GGML_TYPE_F32) {
+        // widening to F32 is lossless and keeps element-wise ops valid (e.g. BF16 adaLN tables in LTX-2.5)
+        return true;
+    }
     if (type != GGML_TYPE_COUNT) {
         if (ggml_is_quantized(type) && tensor_storage.ne[0] % ggml_blck_size(type) != 0) {
             // Pass, do not convert
diff --git a/src/name_conversion.cpp b/src/name_conversion.cpp
index 6a519ba..e1ecdef 100644
--- a/src/name_conversion.cpp
+++ b/src/name_conversion.cpp
@@ -1696,6 +1696,13 @@ std::string convert_tensor_name(std::string name, SDVersion version) {
     replace_with_prefix_map(name, prefix_map);
 
     if (version == VERSION_MOSS_IMAGE) {
+        // The official Diffusers MOSS checkpoint stores Qwen3.5 under
+        // `language_model.*`, while the internal LLM runner uses `model.*`.
+        // GGUF Qwen3.5 checkpoints already use the latter namespace.
+        const std::string hf_language_model_prefix = "text_encoders.llm.language_model.";
+        if (starts_with(name, hf_language_model_prefix)) {
+            name = "text_encoders.llm.model." + name.substr(hf_language_model_prefix.size());
+        }
         const std::string te_prefix = "text_encoders.llm.";
         if (starts_with(name, te_prefix + "blk.")) {
             static const std::vector<std::pair<std::string, std::string>> qwen35_name_map = {
diff --git a/src/stable-diffusion.cpp b/src/stable-diffusion.cpp
index 1cc4c09..50566f5 100644
--- a/src/stable-diffusion.cpp
+++ b/src/stable-diffusion.cpp
@@ -48,7 +48,7 @@ enum sd_type_t str_to_sd_type(const char* str) {
     }
     for (int i = 0; i < std::min<int>(SD_TYPE_COUNT, GGML_TYPE_COUNT); i++) {
         auto trait = ggml_get_type_traits((ggml_type)i);
-        if (!strcmp(str, trait->type_name)) {
+        if (trait->type_name != nullptr && !strcmp(str, trait->type_name)) {
             return (enum sd_type_t)i;
         }
     }
diff --git a/rfx_compat.h b/rfx_compat.h
new file mode 100644
index 0000000..eb36d34
--- /dev/null
+++ b/rfx_compat.h
@@ -0,0 +1,13 @@
+// shims so stable-diffusion.cpp builds against the ROCmFPX fork of ggml (older API)
+#pragma once
+#include "ggml.h"
+#ifdef __cplusplus
+static inline bool ggml_prec_set_acc(struct ggml_tensor * a, enum ggml_prec prec) {
+    switch (a->op) {
+        case GGML_OP_MUL_MAT:
+        case GGML_OP_MUL_MAT_ID:     a->op_params[0] = (int32_t) prec; return true;
+        case GGML_OP_FLASH_ATTN_EXT: a->op_params[3] = (int32_t) prec; return true;
+        default: return false;
+    }
+}
+#endif