Commit af6b5243d for llama.cpp
commit af6b5243d6871923dfecbe06814bd566d9dc383b
Author: Leon <118652405+LeON-Nie-code@users.noreply.github.com>
Date: Wed Nov 11 18:11:06 2026 +0800
webgpu: add PAD_REFLECT_1D support (#31877)
* webgpu : add PAD_REFLECT_1D support
* webgpu: remove redundant PAD_REFLECT_1D contiguity check
* tests: simplify PAD_REFLECT_1D setup
diff --git a/docs/ops.md b/docs/ops.md
index 463d48250..c8204465b 100644
--- a/docs/ops.md
+++ b/docs/ops.md
@@ -86,7 +86,7 @@ Legend:
| OPT_STEP_SGD | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| OUT_PROD | 🟡 | 🟡 | 🟡 | 🟡 | ❌ | ❌ | ❌ | ❌ | 🟡 | 🟡 | ❌ | ❌ | ❌ |
| PAD | ❌ | 🟡 | ✅ | 🟡 | ❌ | ✅ | 🟡 | 🟡 | 🟡 | ✅ | ✅ | ❌ | ❌ |
-| PAD_REFLECT_1D | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
+| PAD_REFLECT_1D | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ |
| POOL_1D | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| POOL_2D | ❌ | 🟡 | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| REGLU | ❌ | ✅ | ✅ | ✅ | 🟡 | 🟡 | 🟡 | 🟡 | ✅ | ✅ | ✅ | ❌ | ❌ |
diff --git a/docs/ops/WebGPU.csv b/docs/ops/WebGPU.csv
index 98720366e..1da1a0f04 100644
--- a/docs/ops/WebGPU.csv
+++ b/docs/ops/WebGPU.csv
@@ -12336,8 +12336,13 @@
"WebGPU: WebGPU","PAD","type=f32,ne_a=[100,1,1,1],pad_0=100,pad_1=0,circular=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","PAD","type=f32,ne_a=[100,1,1,1],pad_0=0,pad_1=100,circular=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","PAD","type=f32,ne_a=[100,100,1,1],pad_0=50,pad_1=50,circular=0","support","1","yes","WebGPU"
-"WebGPU: WebGPU","PAD_REFLECT_1D","type=f32,ne_a=[512,34,2,1],pad_0=10,pad_1=9","support","0","no","WebGPU"
-"WebGPU: WebGPU","PAD_REFLECT_1D","type=f32,ne_a=[3000,384,4,1],pad_0=10,pad_1=9","support","0","no","WebGPU"
+"WebGPU: WebGPU","PAD_REFLECT_1D","type=f32,ne_a=[512,34,2,1],pad_0=10,pad_1=9,view=0","support","1","yes","WebGPU"
+"WebGPU: WebGPU","PAD_REFLECT_1D","type=f32,ne_a=[1,3,2,2],pad_0=0,pad_1=0,view=0","support","1","yes","WebGPU"
+"WebGPU: WebGPU","PAD_REFLECT_1D","type=f32,ne_a=[5,3,2,2],pad_0=4,pad_1=1,view=1","support","1","yes","WebGPU"
+"WebGPU: WebGPU","PAD_REFLECT_1D","type=f32,ne_a=[281,128,1,1],pad_0=2,pad_1=2,view=0","support","1","yes","WebGPU"
+"WebGPU: WebGPU","PAD_REFLECT_1D","type=f32,ne_a=[257,7,1,1],pad_0=0,pad_1=256,view=0","support","1","yes","WebGPU"
+"WebGPU: WebGPU","PAD_REFLECT_1D","type=f32,ne_a=[2,16777217,1,1],pad_0=1,pad_1=1,view=0","support","1","yes","WebGPU"
+"WebGPU: WebGPU","PAD_REFLECT_1D","type=f32,ne_a=[3000,384,4,1],pad_0=10,pad_1=9,view=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","ROLL","shift0=3,shift1=-2,shift3=1,shift4=-1","support","0","no","WebGPU"
"WebGPU: WebGPU","ARANGE","type=f32,start=0.000000,stop=10.000000,step=1.000000","support","1","yes","WebGPU"
"WebGPU: WebGPU","ARANGE","type=f32,start=3.000000,stop=3.500000,step=1.000000","support","1","yes","WebGPU"
diff --git a/ggml/src/ggml-webgpu/ggml-webgpu-shader-lib.hpp b/ggml/src/ggml-webgpu/ggml-webgpu-shader-lib.hpp
index 92ec03fbe..3cc6a70c8 100644
--- a/ggml/src/ggml-webgpu/ggml-webgpu-shader-lib.hpp
+++ b/ggml/src/ggml-webgpu/ggml-webgpu-shader-lib.hpp
@@ -333,14 +333,18 @@ struct ggml_webgpu_rms_norm_mul_shader_decisions {
/** Pad **/
struct ggml_webgpu_pad_pipeline_key {
bool circular;
+ bool reflect;
- bool operator==(const ggml_webgpu_pad_pipeline_key & other) const { return circular == other.circular; }
+ bool operator==(const ggml_webgpu_pad_pipeline_key & other) const {
+ return circular == other.circular && reflect == other.reflect;
+ }
};
struct ggml_webgpu_pad_pipeline_key_hash {
size_t operator()(const ggml_webgpu_pad_pipeline_key & key) const {
size_t seed = 0;
ggml_webgpu_hash_combine(seed, key.circular);
+ ggml_webgpu_hash_combine(seed, key.reflect);
return seed;
}
};
@@ -2047,7 +2051,8 @@ class ggml_webgpu_shader_lib {
webgpu_pipeline get_pad_pipeline(const ggml_webgpu_shader_lib_context & context) {
ggml_webgpu_pad_pipeline_key key = {};
- key.circular = ggml_get_op_params_i32(context.dst, 8) != 0;
+ key.reflect = context.dst->op == GGML_OP_PAD_REFLECT_1D;
+ key.circular = !key.reflect && ggml_get_op_params_i32(context.dst, 8) != 0;
auto it = pad_pipelines.find(key);
if (it != pad_pipelines.end()) {
@@ -2057,6 +2062,10 @@ class ggml_webgpu_shader_lib {
std::vector<std::string> defines;
std::string variant = "pad";
+ if (key.reflect) {
+ defines.push_back("REFLECT");
+ variant += "_reflect";
+ }
if (key.circular) {
defines.push_back("CIRCULAR");
variant += "_circular";
diff --git a/ggml/src/ggml-webgpu/ggml-webgpu.cpp b/ggml/src/ggml-webgpu/ggml-webgpu.cpp
index b827e3d42..c08aa415f 100644
--- a/ggml/src/ggml-webgpu/ggml-webgpu.cpp
+++ b/ggml/src/ggml-webgpu/ggml-webgpu.cpp
@@ -3625,6 +3625,7 @@ static std::optional<webgpu_encoded_op> ggml_webgpu_encode(webgpu_context ctx,
}
case GGML_OP_ARANGE:
return ggml_webgpu_arange(ctx, node);
+ case GGML_OP_PAD_REFLECT_1D:
case GGML_OP_PAD:
return ggml_webgpu_pad(ctx, src0, node);
case GGML_OP_COUNT_EQUAL:
@@ -5165,6 +5166,9 @@ static bool ggml_backend_webgpu_device_supports_op(ggml_backend_dev_t dev, const
case GGML_OP_ARANGE:
supports_op = op->type == GGML_TYPE_F32 && ggml_is_contiguous(op);
break;
+ case GGML_OP_PAD_REFLECT_1D:
+ supports_op = op->type == GGML_TYPE_F32 && src0->type == GGML_TYPE_F32;
+ break;
case GGML_OP_PAD:
supports_op = op->type == GGML_TYPE_F32 && src0->type == GGML_TYPE_F32;
break;
diff --git a/ggml/src/ggml-webgpu/wgsl-shaders/pad.wgsl b/ggml/src/ggml-webgpu/wgsl-shaders/pad.wgsl
index 2cb999bef..6c7b2e0ac 100644
--- a/ggml/src/ggml-webgpu/wgsl-shaders/pad.wgsl
+++ b/ggml/src/ggml-webgpu/wgsl-shaders/pad.wgsl
@@ -64,6 +64,18 @@ fn main(@builtin(num_workgroups) num_wg: vec3<u32>,
var value: f32 = 0.0;
+#ifdef REFLECT
+ var x: u32;
+ if (i0 < params.lp0) {
+ x = params.lp0 - i0;
+ } else if (i0 < params.lp0 + params.src_ne0) {
+ x = i0 - params.lp0;
+ } else {
+ x = params.src_ne0 - 2 - (i0 - params.lp0 - params.src_ne0);
+ }
+ value = src[params.offset_src + x * params.stride_src0 + i1 * params.stride_src1 +
+ i2 * params.stride_src2 + i3 * params.stride_src3];
+#else
#ifdef CIRCULAR
let ci0 = wrap_around(i32(i0) - i32(params.lp0), params.src_ne0);
let ci1 = wrap_around(i32(i1) - i32(params.lp1), params.src_ne1);
@@ -85,5 +97,6 @@ fn main(@builtin(num_workgroups) num_wg: vec3<u32>,
}
#endif
+#endif
dst[params.offset_dst + gid_i] = value;
}
diff --git a/tests/test-backend-ops.cpp b/tests/test-backend-ops.cpp
index 4ab1525d0..ce2bdf0bb 100644
--- a/tests/test-backend-ops.cpp
+++ b/tests/test-backend-ops.cpp
@@ -8843,18 +8843,28 @@ struct test_pad_reflect_1d : public test_case {
const std::array<int64_t, 4> ne_a;
const int pad_0;
const int pad_1;
+ const bool view;
std::string vars() override {
- return VARS_TO_STR4(type, ne_a, pad_0, pad_1);
+ return VARS_TO_STR5(type, ne_a, pad_0, pad_1, view);
}
test_pad_reflect_1d(ggml_type type = GGML_TYPE_F32,
std::array<int64_t, 4> ne_a = {512, 34, 2, 1},
- int pad_0 = 10, int pad_1 = 9)
- : type(type), ne_a(ne_a), pad_0(pad_0), pad_1(pad_1) {}
+ int pad_0 = 10, int pad_1 = 9, bool view = false)
+ : type(type), ne_a(ne_a), pad_0(pad_0), pad_1(pad_1), view(view) {}
ggml_tensor * build_graph(ggml_context * ctx) override {
- ggml_tensor * a = ggml_new_tensor(ctx, type, 2, ne_a.data());
+ ggml_tensor * a;
+ if (view) {
+ const size_t nb1 = ggml_row_size(type, ne_a[0]);
+ const size_t nb2 = nb1 * ne_a[1];
+ const size_t nb3 = nb2 * ne_a[2];
+ ggml_tensor * base = ggml_new_tensor_1d(ctx, type, ne_a[0] * ne_a[1] * ne_a[2] * ne_a[3] + 1);
+ a = ggml_view_4d(ctx, base, ne_a[0], ne_a[1], ne_a[2], ne_a[3], nb1, nb2, nb3, ggml_type_size(type));
+ } else {
+ a = ggml_new_tensor(ctx, type, 4, ne_a.data());
+ }
ggml_set_name(a, "a");
ggml_tensor * out = ggml_pad_reflect_1d(ctx, a, pad_0, pad_1);
@@ -12758,6 +12768,11 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
test_cases.emplace_back(new test_pad_ext(GGML_TYPE_F32, {4, 2, 300, 300}, 1, 1, 0, 0, 0, 0, 0, 0, 0, false));
test_cases.emplace_back(new test_pad_reflect_1d());
+ test_cases.emplace_back(new test_pad_reflect_1d(GGML_TYPE_F32, {1, 3, 2, 2}, 0, 0));
+ test_cases.emplace_back(new test_pad_reflect_1d(GGML_TYPE_F32, {5, 3, 2, 2}, 4, 1, true));
+ test_cases.emplace_back(new test_pad_reflect_1d(GGML_TYPE_F32, {281, 128, 1, 1}, 2, 2));
+ test_cases.emplace_back(new test_pad_reflect_1d(GGML_TYPE_F32, {257, 7, 1, 1}, 0, 256));
+ test_cases.emplace_back(new test_pad_reflect_1d(GGML_TYPE_F32, {2, 16777217, 1, 1}, 1, 1));
test_cases.emplace_back(new test_pad_reflect_1d(GGML_TYPE_F32, {3000, 384, 4, 1}));
test_cases.emplace_back(new test_roll());
test_cases.emplace_back(new test_roll(3, -2, 1, -1, true));