char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Tekin Ertekinggml : fix conv_transpose_2d for multiple batches (#26132)a43c3986b

char/aliceai-t5-moe
25.7 KiB724 linesraw
1#include "common.h"
2
3typedef void (im2col_t)(
4        constant ggml_metal_kargs_im2col & args,
5        device const float * x,
6        device        char * dst,
7        uint3 tgpig[[threadgroup_position_in_grid]],
8        uint3  tgpg[[threadgroups_per_grid]],
9        uint3 tpitg[[thread_position_in_threadgroup]],
10        uint3   ntg[[threads_per_threadgroup]]);
11
12template <typename T>
13kernel void kernel_im2col(
14        constant ggml_metal_kargs_im2col & args,
15        device const float * x,
16        device        char * dst,
17        uint3 tgpig[[threadgroup_position_in_grid]],
18        uint3  tgpg[[threadgroups_per_grid]],
19        uint3 tpitg[[thread_position_in_threadgroup]],
20        uint3   ntg[[threads_per_threadgroup]]) {
21//    const int64_t IC = tgpg[0];
22    const int64_t OH = tgpg[1];
23    const int64_t OW = tgpg[2];
24
25    const int64_t KH = ntg[1];
26    const int64_t KW = ntg[2];
27
28          int64_t in  = tpitg[0];
29    const int64_t ikh = tpitg[1];
30    const int64_t ikw = tpitg[2];
31
32    const int64_t iic = tgpig[0];
33    const int64_t ioh = tgpig[1];
34    const int64_t iow = tgpig[2];
35
36    const int64_t iiw = iow*args.s0 + ikw*args.d0 - args.p0;
37    const int64_t iih = ioh*args.s1 + ikh*args.d1 - args.p1;
38
39    int64_t offset_dst = (in*OH*OW + ioh*OW + iow)*args.CHW + (iic*(KH*KW) + ikh*KW + ikw);
40
41    device T * pdst = (device T *) (dst);
42
43    if (iih < 0 || iih >= args.IH || iiw < 0 || iiw >= args.IW) {
44        while (in < args.N) {
45            pdst[offset_dst] = 0.0f;
46            offset_dst += ntg[0]*args.CHW*OH*OW;
47
48            in += ntg[0];
49        }
50    } else {
51        int64_t offset_src = in*args.ofs0 + iic*args.ofs1 + iih*args.IW + iiw;
52
53        while (in < args.N) {
54            pdst[offset_dst] = x[offset_src];
55
56            offset_dst += ntg[0]*args.CHW*OH*OW;
57            offset_src += ntg[0]*args.ofs0;
58
59            in += ntg[0];
60        }
61    }
62}
63
64template [[host_name("kernel_im2col_f32")]] kernel im2col_t kernel_im2col<float>;
65template [[host_name("kernel_im2col_f16")]] kernel im2col_t kernel_im2col<half>;
66
67// TODO: optimize
68typedef void (im2col_ext_t)(
69        constant ggml_metal_kargs_im2col & args,
70        device const float * x,
71        device        char * dst,
72        uint3 tgpig[[threadgroup_position_in_grid]],
73        uint3  tgpg[[threadgroups_per_grid]],
74        uint3 tpitg[[thread_position_in_threadgroup]],
75        uint3   ntg[[threads_per_threadgroup]]);
76
77template <typename T>
78kernel void kernel_im2col_ext(
79        constant ggml_metal_kargs_im2col & args,
80        device const float * x,
81        device        char * dst,
82        uint3 tgpig[[threadgroup_position_in_grid]],
83        uint3  tgpg[[threadgroups_per_grid]],      // tgpg[0] = D x IC x KH x KW, CHW = IC x KH x KW
84        uint3 tpitg[[thread_position_in_threadgroup]],
85        uint3   ntg[[threads_per_threadgroup]]) {  // [M, 1, 1]
86    const int64_t KHW = (int64_t)args.KHW;
87
88    const int64_t d   = tgpig[0] / args.CHW;
89    const int64_t chw = tgpig[0] % args.CHW;
90    const int64_t tgpig_0 = chw / KHW;  // 0 ~ (IC - 1)
91    const int64_t HW = tgpig[0] % KHW;
92
93    const int64_t tpitg_0 = (d * ntg[0]) + tpitg[0];
94    if (tpitg_0 >= args.N) {
95        return;
96    }
97
98    const int64_t tpitg_1 = HW / args.KW;
99    const int64_t tpitg_2 = HW % args.KW;
100
101    const int64_t iiw = tgpig[2] * args.s0 + tpitg_2 * args.d0 - args.p0;
102    const int64_t iih = tgpig[1] * args.s1 + tpitg_1 * args.d1 - args.p1;
103
104    const int64_t offset_dst =
105        (tpitg_0 * tgpg[1] * tgpg[2] + tgpig[1] * tgpg[2] + tgpig[2]) * args.CHW +
106        (tgpig_0 * KHW + tpitg_1 * args.KW + tpitg_2);
107
108    device T * pdst = (device T *) (dst);
109
110    if (iih < 0 || iih >= args.IH || iiw < 0 || iiw >= args.IW) {
111        pdst[offset_dst] = 0.0f;
112    } else {
113        const int64_t offset_src = tpitg_0 * args.ofs0 + tgpig_0 * args.ofs1;
114        pdst[offset_dst] = x[offset_src + iih * args.IW + iiw];
115    }
116}
117
118template [[host_name("kernel_im2col_ext_f32")]] kernel im2col_ext_t kernel_im2col_ext<float>;
119template [[host_name("kernel_im2col_ext_f16")]] kernel im2col_ext_t kernel_im2col_ext<half>;
120
121template <typename T>
122kernel void kernel_col2im_1d(
123        constant ggml_metal_kargs_col2im_1d & args,
124        device const T * col,
125        device       T * dst,
126        uint         tgpig [[threadgroup_position_in_grid]],
127        uint         tpitg [[thread_position_in_threadgroup]],
128        uint         ntg   [[threads_per_threadgroup]]) {
129
130    const int idx = tgpig * ntg + tpitg;
131    if (idx >= args.T_out * args.OC) {
132        return;
133    }
134
135    const int t_out = idx % args.T_out;
136    const int oc    = idx / args.T_out;
137    const int t_abs = t_out + args.p0;  // absolute position in uncropped signal
138
139    int t_in_min = (t_abs - args.K + args.s0) / args.s0;  // ceil((t_abs - K + 1) / s0)
140    if (t_in_min < 0) {
141        t_in_min = 0;
142    }
143    int t_in_max = t_abs / args.s0;
144    if (t_in_max >= args.T_in) {
145        t_in_max = args.T_in - 1;
146    }
147
148    float sum = 0.0f;
149    for (int t_in = t_in_min; t_in <= t_in_max; t_in++) {
150        const int k = t_abs - t_in * args.s0;
151        sum += float(col[(oc * args.K + k) + t_in * args.K_OC]);
152    }
153
154    dst[t_out + oc * args.T_out] = T(sum);
155}
156
157template [[host_name("kernel_col2im_1d_f32")]]  kernel void kernel_col2im_1d<float>(constant ggml_metal_kargs_col2im_1d &, device const float *, device float *, uint, uint, uint);
158template [[host_name("kernel_col2im_1d_f16")]]  kernel void kernel_col2im_1d<half>(constant ggml_metal_kargs_col2im_1d &, device const half *, device half *, uint, uint, uint);
159#if defined(GGML_METAL_HAS_BF16)
160template [[host_name("kernel_col2im_1d_bf16")]] kernel void kernel_col2im_1d<bfloat>(constant ggml_metal_kargs_col2im_1d &, device const bfloat *, device bfloat *, uint, uint, uint);
161#endif
162
163template <typename TK>
164kernel void kernel_conv_2d(
165        constant ggml_metal_kargs_conv_2d & args,
166        device const char * weights,
167        device const char * src,
168        device       char * dst,
169        uint3   tgpig[[threadgroup_position_in_grid]],
170        uint3    tgpg[[threadgroups_per_grid]],
171        uint3   tpitg[[thread_position_in_threadgroup]],
172        uint3     ntg[[threads_per_threadgroup]]) {
173
174    const uint threads_per_tg = ntg.x * ntg.y * ntg.z;
175    const uint tg_index = (tgpig.z * tgpg.y + tgpig.y) * tgpg.x + tgpig.x;
176    const uint local_thread = tpitg.z * (ntg.x * ntg.y) + tpitg.y * ntg.x + tpitg.x;
177    const uint thread_index = tg_index * threads_per_tg + local_thread;
178    const uint64_t total_threads = (uint64_t) threads_per_tg * tgpg.x * tgpg.y * tgpg.z;
179    const uint64_t total_outputs = (uint64_t) args.N * args.OC * args.OH * args.OW;
180
181    for (uint64_t index = thread_index; index < total_outputs; index += total_threads) {
182        uint64_t tmp = index;
183
184        const int32_t ow = tmp % args.OW; tmp /= args.OW;
185        const int32_t oh = tmp % args.OH; tmp /= args.OH;
186        const int32_t oc = tmp % args.OC; tmp /= args.OC;
187        const int32_t  n = tmp;
188
189        float acc = 0.0f;
190
191        const int32_t base_x = ow*args.s0 - args.p0;
192        const int32_t base_y = oh*args.s1 - args.p1;
193
194        int32_t ky_start = 0;
195        if (base_y < 0) {
196            ky_start = (-base_y + args.d1 - 1)/args.d1;
197        }
198        int32_t ky_end = args.KH;
199        const int32_t y_max = args.IH - 1 - base_y;
200        if (y_max < 0) {
201            ky_end = ky_start;
202        } else if (base_y + (args.KH - 1)*args.d1 >= args.IH) {
203            ky_end = min(ky_end, y_max/args.d1 + 1);
204        }
205
206        int32_t kx_start = 0;
207        if (base_x < 0) {
208            kx_start = (-base_x + args.d0 - 1)/args.d0;
209        }
210        int32_t kx_end = args.KW;
211        const int32_t x_max = args.IW - 1 - base_x;
212        if (x_max < 0) {
213            kx_end = kx_start;
214        } else if (base_x + (args.KW - 1)*args.d0 >= args.IW) {
215            kx_end = min(kx_end, x_max/args.d0 + 1);
216        }
217
218        if (ky_start < ky_end && kx_start < kx_end) {
219            const uint64_t src_base_n = (uint64_t) n  * args.nb13;
220            const uint64_t w_base_oc  = (uint64_t) oc * args.nb03;
221
222            for (int32_t ic = 0; ic < args.IC; ++ic) {
223                const uint64_t src_base_nc = src_base_n + (uint64_t) ic * args.nb12;
224                const uint64_t w_base_ocic = w_base_oc  + (uint64_t) ic * args.nb02;
225
226                for (int32_t ky = ky_start; ky < ky_end; ++ky) {
227                    const int32_t iy = base_y + ky*args.d1;
228                    const uint64_t src_base_row = src_base_nc + (uint64_t) iy * args.nb11;
229                    const uint64_t w_base_row   = w_base_ocic + (uint64_t) ky * args.nb01;
230
231                    for (int32_t kx = kx_start; kx < kx_end; ++kx) {
232                        const int32_t ix = base_x + kx*args.d0;
233                        const uint64_t src_offs = src_base_row + (uint64_t) ix * args.nb10;
234                        const uint64_t w_offs   = w_base_row   + (uint64_t) kx * args.nb00;
235
236                        const float x = *(device const float *)(src + src_offs);
237                        const float w = (float) (*(device const TK *)(weights + w_offs));
238
239                        acc += x * w;
240                    }
241                }
242            }
243        }
244
245        const uint64_t dst_offs =
246            (uint64_t) n  * args.nb3 +
247            (uint64_t) oc * args.nb2 +
248            (uint64_t) oh * args.nb1 +
249            (uint64_t) ow * args.nb0;
250
251        *(device float *)(dst + dst_offs) = acc;
252    }
253}
254
255template [[host_name("kernel_conv_2d_f32_f32")]]
256kernel void kernel_conv_2d<float>(
257        constant ggml_metal_kargs_conv_2d & args,
258        device const char * weights,
259        device const char * src,
260        device       char * dst,
261        uint3   tgpig[[threadgroup_position_in_grid]],
262        uint3    tgpg[[threadgroups_per_grid]],
263        uint3   tpitg[[thread_position_in_threadgroup]],
264        uint3     ntg[[threads_per_threadgroup]]);
265
266template [[host_name("kernel_conv_2d_f16_f32")]]
267kernel void kernel_conv_2d<half>(
268        constant ggml_metal_kargs_conv_2d & args,
269        device const char * weights,
270        device const char * src,
271        device       char * dst,
272        uint3   tgpig[[threadgroup_position_in_grid]],
273        uint3    tgpg[[threadgroups_per_grid]],
274        uint3   tpitg[[thread_position_in_threadgroup]],
275        uint3     ntg[[threads_per_threadgroup]]);
276
277typedef void (conv_transpose_1d_t)(
278        constant ggml_metal_kargs_conv_transpose_1d & args,
279        device const float * src0,
280        device const float * src1,
281        device        char * dst,
282        uint3   tgpig[[threadgroup_position_in_grid]],
283        uint3    tgpg[[threadgroups_per_grid]]);
284
285template <typename T>
286kernel void kernel_conv_transpose_1d(
287        constant ggml_metal_kargs_conv_transpose_1d & args,
288        device const     T * src0,
289        device const float * src1,
290        device        char * dst,
291        uint3   tgpig[[threadgroup_position_in_grid]],
292        uint3   tgpg[[threadgroups_per_grid]]) {
293
294    // For output position j on the time axis, only input positions
295    //   i such that i*s0 <= j < i*s0 + K
296    // contribute -- i.e. i in [ceil((j - K + 1)/s0), floor(j/s0)]
297    // intersected with [0, IL-1]. That's at most ceil(K/s0) values
298    // (typically 2 for stride==K/2 transposed convs).
299    const int32_t j  = tgpig[0];
300    const int32_t s0 = args.s0;
301    const int32_t K  = args.K;
302    const int32_t IL = args.IL;
303
304    int32_t i_min;
305    {
306        int32_t a = j - K + 1;
307        i_min = a <= 0 ? 0 : (a + s0 - 1) / s0; // ceil(a/s0) for a>0
308    }
309    int32_t i_max = j / s0;
310    if (i_max > IL - 1) i_max = IL - 1;
311
312    float v = 0.0f;
313    if (i_min <= i_max) {
314        for (int64_t c = 0; c < args.IC; c++) {
315            const int32_t kernel_offset = c * tgpg[1] * K + K * tgpig[1];
316            const int32_t input_offset  = c * IL;
317
318            for (int32_t i = i_min; i <= i_max; i++) {
319                v += float(src0[kernel_offset + j - i * s0]) * src1[input_offset + i];
320            }
321        }
322    }
323
324    device float * dst_ptr = (device float *) (dst + tgpig[0] * args.nb0 + tgpig[1] * args.nb1);
325
326    dst_ptr[0] = v;
327}
328
329template [[host_name("kernel_conv_transpose_1d_f32_f32")]]
330kernel void kernel_conv_transpose_1d<float>(
331    constant ggml_metal_kargs_conv_transpose_1d & args,
332    device const float * src0,
333    device const float * src1,
334    device        char * dst,
335    uint3   tgpig[[threadgroup_position_in_grid]],
336    uint3    tgpg[[threadgroups_per_grid]]);
337
338template [[host_name("kernel_conv_transpose_1d_f16_f32")]]
339kernel void kernel_conv_transpose_1d<half>(
340    constant ggml_metal_kargs_conv_transpose_1d & args,
341    device const half  * src0,
342    device const float * src1,
343    device        char * dst,
344    uint3   tgpig[[threadgroup_position_in_grid]],
345    uint3    tgpg[[threadgroups_per_grid]]);
346
347
348typedef void (conv_transpose_2d_t)(
349        constant ggml_metal_kargs_conv_transpose_2d & args,
350        device const float * src0,
351        device const float * src1,
352        device        char * dst,
353        uint3   tgpig[[threadgroup_position_in_grid]],
354        uint3    tgpg[[threadgroups_per_grid]]);
355
356template <typename T>
357kernel void kernel_conv_transpose_2d(
358        constant ggml_metal_kargs_conv_transpose_2d & args,
359        device const T * src0,
360        device const float * src1,
361        device        char * dst,
362        threadgroup float * shared_sum [[threadgroup(0)]],
363        uint3   tgpig[[threadgroup_position_in_grid]],
364        uint3   tpitg[[thread_position_in_threadgroup]],
365        uint3     ntg[[threads_per_threadgroup]]) {
366
367    const int64_t out_x = tgpig[0];
368    const int64_t out_y = tgpig[1];
369    const int64_t batch = tgpig[2] / args.OC;
370    const int64_t out_c = tgpig[2] % args.OC;
371
372    const int64_t kw = tpitg[0];
373    const int64_t kh = tpitg[1];
374
375    float v = 0.0f;
376
377    for (int64_t in_c = 0; in_c < args.IC; in_c++) {
378        int64_t in_y = out_y - kh;
379
380        if (in_y < 0 || in_y % args.s0) continue;
381
382        in_y /= args.s0;
383
384        if (in_y >= args.IH) continue;
385
386        int64_t in_x = out_x - kw;
387
388        if (in_x < 0 || in_x % args.s0) continue;
389
390        in_x /= args.s0;
391
392        if (in_x >= args.IW) continue;
393
394        const int64_t input_idx = (args.IW * args.IH) * (args.IC * batch + in_c) + (args.IW) * in_y + in_x;
395        const int64_t kernel_idx = (args.KH * args.KW * args.OC) * in_c + (args.KH * args.KW) * out_c + (args.KW) * kh + kw;
396
397        v += (float)src0[kernel_idx] * src1[input_idx];
398    }
399
400    const uint tid = tpitg.y * ntg.x + tpitg.x;
401    shared_sum[tid] = v;
402
403    threadgroup_barrier(mem_flags::mem_threadgroup);
404
405    if (tid == 0) {
406        float total = 0.0f;
407        const uint num_threads = ntg.x * ntg.y;
408        for (uint i = 0; i < num_threads; i++) {
409            total += shared_sum[i];
410        }
411
412        device float * dst_ptr = (device float *) (dst + batch*args.nb3 + out_c*args.nb2 + out_y * args.nb1 + out_x*args.nb0);
413        dst_ptr[0] = total;
414    }
415}
416
417template [[host_name("kernel_conv_transpose_2d_f32_f32")]]
418kernel void kernel_conv_transpose_2d<float>(
419    constant ggml_metal_kargs_conv_transpose_2d & args,
420    device const float * src0,
421    device const float * src1,
422    device        char * dst,
423    threadgroup float * shared_sum [[threadgroup(0)]],
424    uint3   tgpig[[threadgroup_position_in_grid]],
425    uint3   tpitg[[thread_position_in_threadgroup]],
426    uint3     ntg[[threads_per_threadgroup]]);
427
428template [[host_name("kernel_conv_transpose_2d_f16_f32")]]
429kernel void kernel_conv_transpose_2d<half>(
430    constant ggml_metal_kargs_conv_transpose_2d & args,
431    device const half  * src0,
432    device const float * src1,
433    device        char * dst,
434    threadgroup float * shared_sum [[threadgroup(0)]],
435    uint3   tgpig[[threadgroup_position_in_grid]],
436    uint3   tpitg[[thread_position_in_threadgroup]],
437    uint3     ntg[[threads_per_threadgroup]]);
438
439// grid: x = C tile, y = OH, z = OW * N (for channel-contiguous layouts)
440template <typename TK>
441kernel void kernel_conv_2d_dw_tiled(
442        constant ggml_metal_kargs_conv_2d_dw & args,
443        device const char * weights,
444        device const char * src,
445        device       char * dst,
446        uint3   tgpig[[threadgroup_position_in_grid]],
447        uint3   tpitg[[thread_position_in_threadgroup]],
448        uint3     ntg[[threads_per_threadgroup]]) {
449
450    const int32_t c = (int32_t)(tgpig.x * ntg.x + tpitg.x);
451    if (c >= args.C) {
452        return;
453    }
454
455    const int32_t oh = tgpig.y;
456    const int32_t own = tgpig.z;
457    const int32_t ow = own % args.OW;
458    const int32_t n  = own / args.OW;
459
460    const int32_t base_y = oh*args.s1 - args.p1;
461
462    int32_t ky_start = 0;
463    if (base_y < 0) {
464        ky_start = (-base_y + args.d1 - 1)/args.d1;
465    }
466    int32_t ky_end = args.KH;
467    const int32_t y_max = args.IH - 1 - base_y;
468    if (y_max < 0) {
469        ky_end = ky_start;
470    } else if (base_y + (args.KH - 1)*args.d1 >= args.IH) {
471        ky_end = min(ky_end, y_max/args.d1 + 1);
472    }
473
474    const int32_t base_x = ow*args.s0 - args.p0;
475
476    int32_t kx_start = 0;
477    if (base_x < 0) {
478        kx_start = (-base_x + args.d0 - 1)/args.d0;
479    }
480    int32_t kx_end = args.KW;
481    const int32_t x_max = args.IW - 1 - base_x;
482    if (x_max < 0) {
483        kx_end = kx_start;
484    } else if (base_x + (args.KW - 1)*args.d0 >= args.IW) {
485        kx_end = min(kx_end, x_max/args.d0 + 1);
486    }
487
488    float acc = 0.0f;
489
490    if (ky_start < ky_end && kx_start < kx_end) {
491        const uint64_t w_base   = (uint64_t) c * args.nb02;
492        const uint64_t src_base = (uint64_t) n * args.nb13 + (uint64_t) c * args.nb12;
493
494        for (int32_t ky = ky_start; ky < ky_end; ++ky) {
495            const int32_t iy = base_y + ky*args.d1;
496            const uint64_t src_row = src_base + (uint64_t) iy * args.nb11;
497            const uint64_t w_row = w_base + (uint64_t) ky * args.nb01;
498
499            for (int32_t kx = kx_start; kx < kx_end; ++kx) {
500                const int32_t ix = base_x + kx*args.d0;
501                const float x = *(device const float *)(src + src_row + (uint64_t) ix * args.nb10);
502                const float w = (float)(*(device const TK *)(weights + w_row + (uint64_t) kx * args.nb00));
503                acc += x * w;
504            }
505        }
506    }
507
508    const uint64_t dst_offs =
509        (uint64_t) n  * args.nb3 +
510        (uint64_t) c  * args.nb2 +
511        (uint64_t) oh * args.nb1 +
512        (uint64_t) ow * args.nb0;
513
514    *(device float *)(dst + dst_offs) = acc;
515}
516
517// grid: x = OW tile, y = OH, z = C * N (for spatially-contiguous layouts)
518template <typename TK>
519kernel void kernel_conv_2d_dw(
520        constant ggml_metal_kargs_conv_2d_dw & args,
521        device const char * weights,
522        device const char * src,
523        device       char * dst,
524        uint3   tgpig[[threadgroup_position_in_grid]],
525        uint3   tpitg[[thread_position_in_threadgroup]],
526        uint3     ntg[[threads_per_threadgroup]]) {
527
528    const int32_t oh = tgpig.y;
529    const int32_t cn = tgpig.z;
530    const int32_t c  = cn % args.C;
531    const int32_t n  = cn / args.C;
532
533    const int32_t base_y = oh*args.s1 - args.p1;
534
535    int32_t ky_start = 0;
536    if (base_y < 0) {
537        ky_start = (-base_y + args.d1 - 1)/args.d1;
538    }
539    int32_t ky_end = args.KH;
540    const int32_t y_max = args.IH - 1 - base_y;
541    if (y_max < 0) {
542        ky_end = ky_start;
543    } else if (base_y + (args.KH - 1)*args.d1 >= args.IH) {
544        ky_end = min(ky_end, y_max/args.d1 + 1);
545    }
546
547    const uint64_t w_base   = (uint64_t) c * args.nb02;
548    const uint64_t src_base = (uint64_t) n * args.nb13 + (uint64_t) c * args.nb12;
549
550    const int32_t ow = (int32_t)(tgpig.x * ntg.x + tpitg.x);
551    if (ow >= args.OW) {
552        return;
553    }
554
555    float acc = 0.0f;
556
557    const int32_t base_x = ow*args.s0 - args.p0;
558
559    int32_t kx_start = 0;
560    if (base_x < 0) {
561        kx_start = (-base_x + args.d0 - 1)/args.d0;
562    }
563    int32_t kx_end = args.KW;
564    const int32_t x_max = args.IW - 1 - base_x;
565    if (x_max < 0) {
566        kx_end = kx_start;
567    } else if (base_x + (args.KW - 1)*args.d0 >= args.IW) {
568        kx_end = min(kx_end, x_max/args.d0 + 1);
569    }
570
571    if (ky_start < ky_end && kx_start < kx_end) {
572        for (int32_t ky = ky_start; ky < ky_end; ++ky) {
573            const int32_t iy = base_y + ky*args.d1;
574            const uint64_t src_row = src_base + (uint64_t) iy * args.nb11;
575            const uint64_t w_row = w_base + (uint64_t) ky * args.nb01;
576
577            for (int32_t kx = kx_start; kx < kx_end; ++kx) {
578                const int32_t ix = base_x + kx*args.d0;
579                const float x = *(device const float *)(src + src_row + (uint64_t) ix * args.nb10);
580                const float w = (float)(*(device const TK *)(weights + w_row + (uint64_t) kx * args.nb00));
581                acc += x * w;
582            }
583        }
584    }
585
586    const uint64_t dst_offs =
587        (uint64_t) n  * args.nb3 +
588        (uint64_t) c  * args.nb2 +
589        (uint64_t) oh * args.nb1 +
590        (uint64_t) ow * args.nb0;
591
592    *(device float *)(dst + dst_offs) = acc;
593}
594
595template [[host_name("kernel_conv_2d_dw_f32_f32")]]
596kernel void kernel_conv_2d_dw<float>(
597        constant ggml_metal_kargs_conv_2d_dw & args,
598        device const char * weights,
599        device const char * src,
600        device       char * dst,
601        uint3   tgpig[[threadgroup_position_in_grid]],
602        uint3   tpitg[[thread_position_in_threadgroup]],
603        uint3     ntg[[threads_per_threadgroup]]);
604
605template [[host_name("kernel_conv_2d_dw_f16_f32")]]
606kernel void kernel_conv_2d_dw<half>(
607        constant ggml_metal_kargs_conv_2d_dw & args,
608        device const char * weights,
609        device const char * src,
610        device       char * dst,
611        uint3   tgpig[[threadgroup_position_in_grid]],
612        uint3   tpitg[[thread_position_in_threadgroup]],
613        uint3     ntg[[threads_per_threadgroup]]);
614
615template [[host_name("kernel_conv_2d_dw_tiled_f32_f32")]]
616kernel void kernel_conv_2d_dw_tiled<float>(
617        constant ggml_metal_kargs_conv_2d_dw & args,
618        device const char * weights,
619        device const char * src,
620        device       char * dst,
621        uint3   tgpig[[threadgroup_position_in_grid]],
622        uint3   tpitg[[thread_position_in_threadgroup]],
623        uint3     ntg[[threads_per_threadgroup]]);
624
625template [[host_name("kernel_conv_2d_dw_tiled_f16_f32")]]
626kernel void kernel_conv_2d_dw_tiled<half>(
627        constant ggml_metal_kargs_conv_2d_dw & args,
628        device const char * weights,
629        device const char * src,
630        device       char * dst,
631        uint3   tgpig[[threadgroup_position_in_grid]],
632        uint3   tpitg[[thread_position_in_threadgroup]],
633        uint3     ntg[[threads_per_threadgroup]]);
634
635template <typename T>
636kernel void kernel_conv_3d(
637        constant ggml_metal_kargs_conv_3d & args,
638        device const  char * src0, // Weights [IC * OC, KD, KH, KW]
639        device const  char * src1, // Inputs  [IC * N,  ID, IH, IW]
640        device       char  * dst,  // Outputs [OC * N,  OD, OH, OW]
641        uint3 tgpig[[threadgroup_position_in_grid]],
642        uint3 tpitg[[thread_position_in_threadgroup]]) {
643
644    // 1. Un-flatten the spatial dimension from Grid X
645    int64_t spatial_idx = tgpig.x * 32 + tpitg.x;
646
647    if (spatial_idx >= args.OW * args.OH * args.OD) {
648        return; // Thread falls outside the spatial volume
649    }
650
651    int64_t od = spatial_idx / (args.OW * args.OH);
652    int64_t oh = (spatial_idx / args.OW) % args.OH;
653    int64_t ow = spatial_idx % args.OW;
654
655    // 2. Map Y to Channels, Z to Batch
656    int64_t oc = tgpig.y;
657    int64_t batch_idx = tgpig.z;
658
659    // 3. Calculate anchor coordinates in the Input volume
660    int64_t i_w_base = ow * args.s0 - args.p0;
661    int64_t i_h_base = oh * args.s1 - args.p1;
662    int64_t i_d_base = od * args.s2 - args.p2;
663
664    float sum = 0.0f;
665
666    // 4. Gather Loop (Iterate over Input Channels -> Depth -> Height -> Width)
667    for (int64_t ic = 0; ic < args.IC; ++ic) {
668
669        // ggml packs batch and channel together in the 4th dimension
670        int64_t src_cn_idx = batch_idx * args.IC + ic;
671        int64_t w_cn_idx   = oc * args.IC + ic;
672
673        for (int64_t kz = 0; kz < args.KD; ++kz) {
674            int64_t id = i_d_base + kz * args.d2;
675            if (id < 0 || id >= args.ID) continue; // Boundary check (Padding)
676
677            for (int64_t ky = 0; ky < args.KH; ++ky) {
678                int64_t ih = i_h_base + ky * args.d1;
679                if (ih < 0 || ih >= args.IH) continue;
680
681                for (int64_t kx = 0; kx < args.KW; ++kx) {
682                    int64_t iw = i_w_base + kx * args.d0;
683                    if (iw < 0 || iw >= args.IW) continue;
684
685                    // Convert multi-dimensional coordinates to flat byte offsets
686                    int64_t w_idx = kx*args.nb00 + ky*args.nb01 + kz*args.nb02 + w_cn_idx*args.nb03;
687                    int64_t i_idx = iw*args.nb10 + ih*args.nb11 + id*args.nb12 + src_cn_idx*args.nb13;
688
689                    // Dereference memory and cast weights to f32 if they were f16
690                    float w_val = (float)*(device const T*)((device const char*)src0 + w_idx);
691                    float i_val = *(device const float*)((device const char*)src1 + i_idx);
692
693                    sum += w_val * i_val;
694                }
695            }
696        }
697    }
698
699    // 5. Write the accumulated value out to RAM
700    int64_t dst_cn_idx = batch_idx * args.OC + oc;
701    int64_t d_idx = ow*args.nb0 + oh*args.nb1 + od*args.nb2 + dst_cn_idx*args.nb3;
702
703    *(device float*)(dst + d_idx) = sum;
704}
705
706// Explicit instantiations so the JIT compiler can find them by name
707template [[host_name("kernel_conv_3d_f32_f32")]]
708kernel void kernel_conv_3d<float>(
709    constant ggml_metal_kargs_conv_3d & args,
710    device const char * src0,
711    device const char * src1,
712    device       char  * dst,
713    uint3 tgpig[[threadgroup_position_in_grid]],
714    uint3 tpitg[[thread_position_in_threadgroup]]);
715
716// Explicit instantiation for f16 weights
717template [[host_name("kernel_conv_3d_f16_f32")]]
718kernel void kernel_conv_3d<half>(
719    constant ggml_metal_kargs_conv_3d & args,
720    device const char  * src0,
721    device const char * src1,
722    device       char  * dst,
723    uint3 tgpig[[threadgroup_position_in_grid]],
724    uint3 tpitg[[thread_position_in_threadgroup]]);