| 120 | } |
| 121 | |
| 122 | VARP WanDiffusion::encodePrompt(const std::string& prompt, int* seqLen, VARP* outMask) { |
| 123 | if (!mTokenizer || mModules.size() < 1 || !mModules[0]) { |
| 124 | MNN_ERROR("Wan text encoder is not ready\n"); |
| 125 | return nullptr; |
| 126 | } |
| 127 | |
| 128 | // Defensive: peek at the un-padded token count first so we can warn the |
| 129 | // caller when the prompt is truncated. mTokenizer->encode pads/truncates |
| 130 | // silently when maxlen > 0; passing maxlen=0 returns the raw ids. |
| 131 | std::vector<int> rawTokens = mTokenizer->encode(prompt, 0); |
| 132 | if ((int)rawTokens.size() > mMaxTextLen) { |
| 133 | MNN_PRINT("Wan: prompt truncated from %d to %d tokens\n", (int)rawTokens.size(), mMaxTextLen); |
| 134 | } |
| 135 | |
| 136 | std::vector<int> uncond = mTokenizer->encode("", mMaxTextLen); |
| 137 | std::vector<int> cond = mTokenizer->encode(prompt, mMaxTextLen); |
| 138 | if ((int)uncond.size() < mMaxTextLen || (int)cond.size() < mMaxTextLen) { |
| 139 | MNN_ERROR("Wan tokenizer returned too few ids: uncond=%d cond=%d expected=%d\n", (int)uncond.size(), |
| 140 | (int)cond.size(), mMaxTextLen); |
| 141 | return nullptr; |
| 142 | } |
| 143 | |
| 144 | VARP inputIds = _Input({2, mMaxTextLen}, NCHW, halide_type_of<int>()); |
| 145 | int* inputPtr = inputIds->writeMap<int>(); |
| 146 | ::memset(inputPtr, 0, 2 * mMaxTextLen * sizeof(int)); |
| 147 | ::memcpy(inputPtr, uncond.data(), mMaxTextLen * sizeof(int)); |
| 148 | ::memcpy(inputPtr + mMaxTextLen, cond.data(), mMaxTextLen * sizeof(int)); |
| 149 | |
| 150 | // Build attention mask from token ids: 1 where id != 0, 0 for padding. |
| 151 | // Dtype must be int32 to match the ONNX export (see wan_onnx_export.py). |
| 152 | VARP mask = _Input({2, mMaxTextLen}, NCHW, halide_type_of<int>()); |
| 153 | int* maskData = mask->writeMap<int>(); |
| 154 | for (int i = 0; i < 2 * mMaxTextLen; ++i) { |
| 155 | maskData[i] = (inputPtr[i] != 0) ? 1 : 0; |
| 156 | } |
| 157 | if (outMask) { |
| 158 | *outMask = mask; |
| 159 | } |
| 160 | |
| 161 | auto outputs = mModules[0]->onForward({inputIds}); |
| 162 | if (outputs.empty() || outputs[0].get() == nullptr) { |
| 163 | MNN_ERROR("Wan text_encoder returned empty output\n"); |
| 164 | return nullptr; |
| 165 | } |
| 166 | auto hiddenStates = _Convert(outputs[0], NCHW); |
| 167 | auto info = hiddenStates->getInfo(); |
| 168 | if (seqLen != nullptr && info != nullptr && info->dim.size() > 1) { |
| 169 | *seqLen = info->dim[1]; |
| 170 | } else if (seqLen != nullptr) { |
| 171 | *seqLen = mMaxTextLen; |
| 172 | } |
| 173 | hiddenStates.fix(VARP::CONSTANT); |
| 174 | return hiddenStates; |
| 175 | } |
| 176 | |
| 177 | VARP WanDiffusion::transformer(VARP hiddenStates, VARP timestep, VARP encoderHiddenStates, VARP encoderAttentionMask) { |
| 178 | auto outputs = mModules[1]->onForward({hiddenStates, timestep, encoderHiddenStates, encoderAttentionMask}); |