|
@@ -222,6 +222,7 @@ enum vk_device_architecture {
|
|
|
AMD_RDNA2,
|
|
AMD_RDNA2,
|
|
|
AMD_RDNA3,
|
|
AMD_RDNA3,
|
|
|
INTEL_XE2,
|
|
INTEL_XE2,
|
|
|
|
|
+ NVIDIA_PRE_TURING,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// HSK x HSV
|
|
// HSK x HSV
|
|
@@ -315,10 +316,33 @@ static vk_device_architecture get_device_architecture(const vk::PhysicalDevice&
|
|
|
// https://www.intel.com/content/www/us/en/docs/oneapi/optimization-guide-gpu/2025-0/intel-xe-gpu-architecture.html
|
|
// https://www.intel.com/content/www/us/en/docs/oneapi/optimization-guide-gpu/2025-0/intel-xe-gpu-architecture.html
|
|
|
return vk_device_architecture::INTEL_XE2;
|
|
return vk_device_architecture::INTEL_XE2;
|
|
|
}
|
|
}
|
|
|
|
|
+ } else if (props.vendorID == VK_VENDOR_ID_NVIDIA) {
|
|
|
|
|
+ const std::vector<vk::ExtensionProperties> ext_props = device.enumerateDeviceExtensionProperties();
|
|
|
|
|
+
|
|
|
|
|
+ bool cooperative_matrix = false;
|
|
|
|
|
+
|
|
|
|
|
+ // Detect "pre-turing" based on lack of coopmat support.
|
|
|
|
|
+ for (const auto& properties : ext_props) {
|
|
|
|
|
+ if (strcmp("VK_KHR_cooperative_matrix", properties.extensionName) == 0) {
|
|
|
|
|
+ cooperative_matrix = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!cooperative_matrix) {
|
|
|
|
|
+ return vk_device_architecture::NVIDIA_PRE_TURING;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
return vk_device_architecture::OTHER;
|
|
return vk_device_architecture::OTHER;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+enum vk_conv_shapes {
|
|
|
|
|
+ CONV_SHAPE_128x128,
|
|
|
|
|
+ CONV_SHAPE_64x32,
|
|
|
|
|
+ CONV_SHAPE_32x256,
|
|
|
|
|
+ CONV_SHAPE_COUNT,
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
struct vk_device_struct {
|
|
struct vk_device_struct {
|
|
|
std::recursive_mutex mutex;
|
|
std::recursive_mutex mutex;
|
|
|
|
|
|
|
@@ -483,8 +507,8 @@ struct vk_device_struct {
|
|
|
vk_pipeline pipeline_rwkv_wkv6_f32;
|
|
vk_pipeline pipeline_rwkv_wkv6_f32;
|
|
|
vk_pipeline pipeline_rwkv_wkv7_f32;
|
|
vk_pipeline pipeline_rwkv_wkv7_f32;
|
|
|
vk_pipeline pipeline_opt_step_adamw_f32;
|
|
vk_pipeline pipeline_opt_step_adamw_f32;
|
|
|
- vk_pipeline pipeline_conv2d_f32;
|
|
|
|
|
- vk_pipeline pipeline_conv2d_f16_f32;
|
|
|
|
|
|
|
+ vk_pipeline pipeline_conv2d_f32[CONV_SHAPE_COUNT];
|
|
|
|
|
+ vk_pipeline pipeline_conv2d_f16_f32[CONV_SHAPE_COUNT];
|
|
|
vk_pipeline pipeline_conv2d_dw_whcn_f32;
|
|
vk_pipeline pipeline_conv2d_dw_whcn_f32;
|
|
|
vk_pipeline pipeline_conv2d_dw_cwhn_f32;
|
|
vk_pipeline pipeline_conv2d_dw_cwhn_f32;
|
|
|
|
|
|
|
@@ -908,8 +932,22 @@ struct vk_op_conv2d_push_constants {
|
|
|
uint32_t nb1;
|
|
uint32_t nb1;
|
|
|
uint32_t nb2;
|
|
uint32_t nb2;
|
|
|
uint32_t nb3;
|
|
uint32_t nb3;
|
|
|
|
|
+
|
|
|
|
|
+ // init_fastdiv_values constants for dividing by KW, KW*KH, OW, OW*OH
|
|
|
|
|
+ uint32_t KWmp; uint32_t KWL;
|
|
|
|
|
+ uint32_t KWKHmp; uint32_t KWKHL;
|
|
|
|
|
+ uint32_t OWmp; uint32_t OWL;
|
|
|
|
|
+ uint32_t OWOHmp; uint32_t OWOHL;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+template <> void init_pushconst_fastdiv(vk_op_conv2d_push_constants &p) {
|
|
|
|
|
+ // Compute magic values to divide by KW, KW*KH, OW, OW*OH
|
|
|
|
|
+ init_fastdiv_values(p.KW, p.KWmp, p.KWL);
|
|
|
|
|
+ init_fastdiv_values(p.KW*p.KH, p.KWKHmp, p.KWKHL);
|
|
|
|
|
+ init_fastdiv_values(p.OW, p.OWmp, p.OWL);
|
|
|
|
|
+ init_fastdiv_values(p.OW*p.OH, p.OWOHmp, p.OWOHL);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
struct vk_op_conv2d_dw_push_constants {
|
|
struct vk_op_conv2d_dw_push_constants {
|
|
|
uint32_t ne;
|
|
uint32_t ne;
|
|
|
uint32_t batches;
|
|
uint32_t batches;
|
|
@@ -3048,48 +3086,89 @@ static void ggml_vk_load_shaders(vk_device& device) {
|
|
|
ggml_vk_create_pipeline(device, device->pipeline_opt_step_adamw_f32, "opt_step_adamw_f32", opt_step_adamw_f32_len, opt_step_adamw_f32_data, "main", 5, sizeof(vk_op_push_constants), {512, 1, 1}, {}, 1);
|
|
ggml_vk_create_pipeline(device, device->pipeline_opt_step_adamw_f32, "opt_step_adamw_f32", opt_step_adamw_f32_len, opt_step_adamw_f32_data, "main", 5, sizeof(vk_op_push_constants), {512, 1, 1}, {}, 1);
|
|
|
|
|
|
|
|
// conv2d
|
|
// conv2d
|
|
|
- uint32_t conv2d_WG_SIZE = 256;
|
|
|
|
|
- uint32_t conv2d_BS_K = 128;
|
|
|
|
|
- uint32_t conv2d_BS_CRS = 16;
|
|
|
|
|
- uint32_t use_collectives = 0; // Enables subgroup ops for preventing the re-calculation of indices.
|
|
|
|
|
- if (device->subgroup_shuffle &&
|
|
|
|
|
- device->vendor_id != VK_VENDOR_ID_INTEL) { // Do not enable collectives on Intel, see PR 14316
|
|
|
|
|
- use_collectives = 1;
|
|
|
|
|
- conv2d_BS_CRS = std::min(
|
|
|
|
|
- device->subgroup_size,
|
|
|
|
|
- conv2d_BS_CRS); // CRS block size should be capped at sugroup size for correctness when shuffle is used.
|
|
|
|
|
- }
|
|
|
|
|
- uint32_t conv2d_BS_NPQ = 128;
|
|
|
|
|
- uint32_t conv2d_TS_K = 8;
|
|
|
|
|
- uint32_t conv2d_shmem_req =
|
|
|
|
|
- (conv2d_BS_K * (conv2d_BS_CRS + 1) + conv2d_BS_CRS * (conv2d_BS_NPQ + 1)) * sizeof(float);
|
|
|
|
|
- if (device->properties.limits.maxComputeSharedMemorySize < conv2d_shmem_req) {
|
|
|
|
|
- conv2d_BS_CRS = 8;
|
|
|
|
|
- if (use_collectives) {
|
|
|
|
|
- conv2d_BS_CRS = std::min(device->subgroup_size, conv2d_BS_CRS);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (use_collectives) {
|
|
|
|
|
- ggml_vk_create_pipeline(
|
|
|
|
|
- device, device->pipeline_conv2d_f32, "conv2d_f32", conv2d_f32_len, conv2d_f32_data, "main", 3,
|
|
|
|
|
- sizeof(vk_op_conv2d_push_constants), { conv2d_BS_K, conv2d_BS_NPQ, 1 },
|
|
|
|
|
- { conv2d_WG_SIZE, conv2d_BS_K, conv2d_BS_CRS, conv2d_BS_NPQ, conv2d_TS_K, use_collectives }, 1, true, true);
|
|
|
|
|
- ggml_vk_create_pipeline(
|
|
|
|
|
- device, device->pipeline_conv2d_f16_f32, "conv2d_f16_f32", conv2d_f16_f32_len, conv2d_f16_f32_data, "main", 3,
|
|
|
|
|
- sizeof(vk_op_conv2d_push_constants), { conv2d_BS_K, conv2d_BS_NPQ, 1 },
|
|
|
|
|
- { conv2d_WG_SIZE, conv2d_BS_K, conv2d_BS_CRS, conv2d_BS_NPQ, conv2d_TS_K, use_collectives }, 1, true, true);
|
|
|
|
|
- } else {
|
|
|
|
|
- ggml_vk_create_pipeline(
|
|
|
|
|
- device, device->pipeline_conv2d_f32, "conv2d_f32", conv2d_f32_len, conv2d_f32_data, "main", 3,
|
|
|
|
|
- sizeof(vk_op_conv2d_push_constants), { conv2d_BS_K, conv2d_BS_NPQ, 1 },
|
|
|
|
|
- { conv2d_WG_SIZE, conv2d_BS_K, conv2d_BS_CRS, conv2d_BS_NPQ, conv2d_TS_K, use_collectives }, 1, true,
|
|
|
|
|
- false);
|
|
|
|
|
- ggml_vk_create_pipeline(
|
|
|
|
|
- device, device->pipeline_conv2d_f16_f32, "conv2d_f16_f32", conv2d_f16_f32_len, conv2d_f16_f32_data, "main", 3,
|
|
|
|
|
- sizeof(vk_op_conv2d_push_constants), { conv2d_BS_K, conv2d_BS_NPQ, 1 },
|
|
|
|
|
- { conv2d_WG_SIZE, conv2d_BS_K, conv2d_BS_CRS, conv2d_BS_NPQ, conv2d_TS_K, use_collectives }, 1, true,
|
|
|
|
|
- false);
|
|
|
|
|
|
|
+ for (uint32_t s = 0; s < CONV_SHAPE_COUNT; ++s) {
|
|
|
|
|
+ uint32_t conv2d_WG_SIZE = 256;
|
|
|
|
|
+ uint32_t conv2d_BS_K = 128;
|
|
|
|
|
+ uint32_t conv2d_BS_CRS = 16;
|
|
|
|
|
+ uint32_t use_collectives = 0; // Enables subgroup ops for preventing the re-calculation of indices.
|
|
|
|
|
+ uint32_t conv2d_BS_NPQ = 128;
|
|
|
|
|
+ uint32_t conv2d_TS_K = 8;
|
|
|
|
|
+ uint32_t conv2d_SHMEM_PAD = 4;
|
|
|
|
|
+ bool conv2d_UNROLL = true;
|
|
|
|
|
+
|
|
|
|
|
+ if (device->vendor_id == VK_VENDOR_ID_INTEL) {
|
|
|
|
|
+ conv2d_SHMEM_PAD = 0;
|
|
|
|
|
+ conv2d_UNROLL = false;
|
|
|
|
|
+ } else if (device->vendor_id == VK_VENDOR_ID_AMD) {
|
|
|
|
|
+ conv2d_SHMEM_PAD = device->architecture == vk_device_architecture::AMD_GCN ? 1 : 4;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ switch (s) {
|
|
|
|
|
+ default:
|
|
|
|
|
+ case CONV_SHAPE_128x128:
|
|
|
|
|
+ conv2d_BS_K = 128;
|
|
|
|
|
+ conv2d_BS_NPQ = 128;
|
|
|
|
|
+ conv2d_BS_CRS = 16;
|
|
|
|
|
+ if (device->vendor_id == VK_VENDOR_ID_AMD && device->architecture != vk_device_architecture::AMD_GCN) {
|
|
|
|
|
+ conv2d_UNROLL = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ case CONV_SHAPE_64x32:
|
|
|
|
|
+ conv2d_BS_K = 64;
|
|
|
|
|
+ conv2d_BS_NPQ = 32;
|
|
|
|
|
+ conv2d_BS_CRS = 32;
|
|
|
|
|
+ conv2d_TS_K = 4;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case CONV_SHAPE_32x256:
|
|
|
|
|
+ conv2d_BS_K = 32;
|
|
|
|
|
+ conv2d_BS_NPQ = 256;
|
|
|
|
|
+ conv2d_BS_CRS = 16;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Use collectives on pre-Turing NVIDIA GPUs and GCN AMD cards, which had slower integer math.
|
|
|
|
|
+ bool allow_collectives_nv = device->vendor_id != VK_VENDOR_ID_NVIDIA ||
|
|
|
|
|
+ device->architecture == vk_device_architecture::NVIDIA_PRE_TURING;
|
|
|
|
|
+ bool allow_collectives_amd = device->vendor_id != VK_VENDOR_ID_AMD ||
|
|
|
|
|
+ device->architecture == vk_device_architecture::AMD_GCN;
|
|
|
|
|
+
|
|
|
|
|
+ if (device->subgroup_shuffle &&
|
|
|
|
|
+ device->vendor_id != VK_VENDOR_ID_INTEL && // Do not enable collectives on Intel, see PR 14316.
|
|
|
|
|
+ allow_collectives_nv &&
|
|
|
|
|
+ allow_collectives_amd) {
|
|
|
|
|
+ use_collectives = 1;
|
|
|
|
|
+ conv2d_BS_CRS = std::min(
|
|
|
|
|
+ device->subgroup_size,
|
|
|
|
|
+ conv2d_BS_CRS); // CRS block size should be capped at subgroup size for correctness when shuffle is used.
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ uint32_t conv2d_shmem_req =
|
|
|
|
|
+ (conv2d_BS_K * (conv2d_BS_CRS + conv2d_SHMEM_PAD) + conv2d_BS_CRS * (conv2d_BS_NPQ + conv2d_SHMEM_PAD)) * sizeof(float);
|
|
|
|
|
+ if (device->properties.limits.maxComputeSharedMemorySize < conv2d_shmem_req) {
|
|
|
|
|
+ conv2d_BS_CRS = 8;
|
|
|
|
|
+ if (use_collectives) {
|
|
|
|
|
+ conv2d_BS_CRS = std::min(device->subgroup_size, conv2d_BS_CRS);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::array<uint32_t, 3> wg_denoms = { conv2d_BS_K, conv2d_BS_NPQ, 1 };
|
|
|
|
|
+ std::vector<uint32_t> spec_constants = { conv2d_WG_SIZE, conv2d_BS_K, conv2d_BS_CRS, conv2d_BS_NPQ, conv2d_TS_K, use_collectives, conv2d_SHMEM_PAD };
|
|
|
|
|
+
|
|
|
|
|
+ if (conv2d_UNROLL) {
|
|
|
|
|
+ ggml_vk_create_pipeline(
|
|
|
|
|
+ device, device->pipeline_conv2d_f32[s], "conv2d_f32", conv2d_f32_unroll_len, conv2d_f32_unroll_data, "main", 3,
|
|
|
|
|
+ sizeof(vk_op_conv2d_push_constants), wg_denoms, spec_constants, 1, true, use_collectives);
|
|
|
|
|
+ ggml_vk_create_pipeline(
|
|
|
|
|
+ device, device->pipeline_conv2d_f16_f32[s], "conv2d_f16_f32", conv2d_f16_f32_unroll_len, conv2d_f16_f32_unroll_data, "main", 3,
|
|
|
|
|
+ sizeof(vk_op_conv2d_push_constants), wg_denoms, spec_constants, 1, true, use_collectives);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ggml_vk_create_pipeline(
|
|
|
|
|
+ device, device->pipeline_conv2d_f32[s], "conv2d_f32", conv2d_f32_len, conv2d_f32_data, "main", 3,
|
|
|
|
|
+ sizeof(vk_op_conv2d_push_constants), wg_denoms, spec_constants, 1, true, use_collectives);
|
|
|
|
|
+ ggml_vk_create_pipeline(
|
|
|
|
|
+ device, device->pipeline_conv2d_f16_f32[s], "conv2d_f16_f32", conv2d_f16_f32_len, conv2d_f16_f32_data, "main", 3,
|
|
|
|
|
+ sizeof(vk_op_conv2d_push_constants), wg_denoms, spec_constants, 1, true, use_collectives);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ggml_vk_create_pipeline(device, device->pipeline_conv2d_dw_whcn_f32, "conv2d_dw_whcn_f32", conv2d_dw_whcn_f32_len, conv2d_dw_whcn_f32_data, "main", 3, sizeof(vk_op_conv2d_dw_push_constants), {512, 1, 1}, {}, 1);
|
|
ggml_vk_create_pipeline(device, device->pipeline_conv2d_dw_whcn_f32, "conv2d_dw_whcn_f32", conv2d_dw_whcn_f32_len, conv2d_dw_whcn_f32_data, "main", 3, sizeof(vk_op_conv2d_dw_push_constants), {512, 1, 1}, {}, 1);
|
|
@@ -6641,6 +6720,34 @@ static void ggml_vk_flash_attn(ggml_backend_vk_context * ctx, vk_context& subctx
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+static std::array<uint32_t, 3> ggml_vk_get_conv_elements(const ggml_tensor *dst) {
|
|
|
|
|
+ const ggml_tensor *src0 = dst->src[0];
|
|
|
|
|
+ const ggml_tensor *src1 = dst->src[1];
|
|
|
|
|
+
|
|
|
|
|
+ // src0 - kernel: [KW, KH, Cin, Cout]
|
|
|
|
|
+ // src1 - input: [W, H, Cin, N]
|
|
|
|
|
+ // dst - result: [OW, OH, Cout, N]
|
|
|
|
|
+
|
|
|
|
|
+ // Copied from ggml.c: int64_t ggml_calc_conv_output_size(int64_t ins, int64_t ks, int s, int p, int d)
|
|
|
|
|
+ auto calc_conv_output_size = [](int64_t ins, int64_t ks, int s, int p, int d) -> int64_t {
|
|
|
|
|
+ return (ins + 2 * p - d * (ks - 1) - 1) / s + 1;
|
|
|
|
|
+ };
|
|
|
|
|
+ // parallelize in {OW/BS_K, OH/BS_NPQ, 1}
|
|
|
|
|
+ int64_t W = src1->ne[0];
|
|
|
|
|
+ int64_t H = src1->ne[1];
|
|
|
|
|
+ int64_t KW = src0->ne[0];
|
|
|
|
|
+ int64_t KH = src0->ne[1];
|
|
|
|
|
+ int64_t Cout = src0->ne[3];
|
|
|
|
|
+ int64_t N = src1->ne[3];
|
|
|
|
|
+ int64_t OH = calc_conv_output_size(H, KH, dst->op_params[1], dst->op_params[3], dst->op_params[5]);
|
|
|
|
|
+ int64_t OW = calc_conv_output_size(W, KW, dst->op_params[0], dst->op_params[2], dst->op_params[4]);
|
|
|
|
|
+ int64_t NPQ = N * OW * OH;
|
|
|
|
|
+
|
|
|
|
|
+ // Tile output matrix to (K/NB_K, NPQ/NB_NPQ, 1) workgroups
|
|
|
|
|
+ std::array<uint32_t, 3> elements = { static_cast<uint32_t>(Cout), static_cast<uint32_t>(NPQ), 1 };
|
|
|
|
|
+ return elements;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst, ggml_op op) {
|
|
static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst, ggml_op op) {
|
|
|
switch (op) {
|
|
switch (op) {
|
|
|
case GGML_OP_GET_ROWS:
|
|
case GGML_OP_GET_ROWS:
|
|
@@ -6970,10 +7077,30 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const
|
|
|
case GGML_OP_CONV_2D:
|
|
case GGML_OP_CONV_2D:
|
|
|
if (src1->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32 &&
|
|
if (src1->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32 &&
|
|
|
ggml_is_contiguous(src0) && ggml_is_contiguous(src1) && ggml_is_contiguous(dst)) {
|
|
ggml_is_contiguous(src0) && ggml_is_contiguous(src1) && ggml_is_contiguous(dst)) {
|
|
|
|
|
+ auto elements = ggml_vk_get_conv_elements(dst);
|
|
|
|
|
+ vk_conv_shapes shape;
|
|
|
|
|
+
|
|
|
|
|
+ uint32_t tiles[CONV_SHAPE_COUNT];
|
|
|
|
|
+ for (uint32_t i = 0; i < CONV_SHAPE_COUNT; ++i) {
|
|
|
|
|
+ tiles[i] = CEIL_DIV(elements[0], ctx->device->pipeline_conv2d_f32[i]->wg_denoms[0]) * CEIL_DIV(elements[1], ctx->device->pipeline_conv2d_f32[i]->wg_denoms[1]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // We can't query number of shader cores on Intel, use 32 as a placeholder
|
|
|
|
|
+ // so small convolutions will still choose a smaller tile.
|
|
|
|
|
+ const uint32_t shader_core_count = ctx->device->shader_core_count > 0 ? ctx->device->shader_core_count : 32;
|
|
|
|
|
+
|
|
|
|
|
+ if (elements[0] > 64 && tiles[CONV_SHAPE_128x128] >= shader_core_count * 2) {
|
|
|
|
|
+ shape = CONV_SHAPE_128x128;
|
|
|
|
|
+ } else if (elements[0] <= 32 && tiles[CONV_SHAPE_32x256] >= shader_core_count * 2) {
|
|
|
|
|
+ shape = CONV_SHAPE_32x256;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ shape = CONV_SHAPE_64x32;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (src0->type == GGML_TYPE_F32) {
|
|
if (src0->type == GGML_TYPE_F32) {
|
|
|
- return ctx->device->pipeline_conv2d_f32;
|
|
|
|
|
|
|
+ return ctx->device->pipeline_conv2d_f32[shape];
|
|
|
} else if (src0->type == GGML_TYPE_F16) {
|
|
} else if (src0->type == GGML_TYPE_F16) {
|
|
|
- return ctx->device->pipeline_conv2d_f16_f32;
|
|
|
|
|
|
|
+ return ctx->device->pipeline_conv2d_f16_f32[shape];
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
return nullptr;
|
|
return nullptr;
|
|
@@ -7301,29 +7428,8 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co
|
|
|
} break;
|
|
} break;
|
|
|
case GGML_OP_CONV_2D:
|
|
case GGML_OP_CONV_2D:
|
|
|
{
|
|
{
|
|
|
- // src0 - kernel: [KW, KH, Cin, Cout]
|
|
|
|
|
- // src1 - input: [W, H, Cin, N]
|
|
|
|
|
- // dst - result: [OW, OH, Cout, N]
|
|
|
|
|
-
|
|
|
|
|
- // Copied from ggml.c: int64_t ggml_calc_conv_output_size(int64_t ins, int64_t ks, int s, int p, int d)
|
|
|
|
|
- auto calc_conv_output_size = [](int64_t ins, int64_t ks, int s, int p, int d) -> int64_t {
|
|
|
|
|
- return (ins + 2 * p - d * (ks - 1) - 1) / s + 1;
|
|
|
|
|
- };
|
|
|
|
|
- // parallelize in {OW/BS_K, OH/BS_NPQ, 1}
|
|
|
|
|
- int64_t W = src1->ne[0];
|
|
|
|
|
- int64_t H = src1->ne[1];
|
|
|
|
|
- int64_t KW = src0->ne[0];
|
|
|
|
|
- int64_t KH = src0->ne[1];
|
|
|
|
|
- int64_t Cout = src0->ne[3];
|
|
|
|
|
- int64_t N = src1->ne[3];
|
|
|
|
|
- int64_t OH = calc_conv_output_size(H, KH, dst->op_params[1], dst->op_params[3], dst->op_params[5]);
|
|
|
|
|
- int64_t OW = calc_conv_output_size(W, KW, dst->op_params[0], dst->op_params[2], dst->op_params[4]);
|
|
|
|
|
- int64_t NPQ = N * OW * OH;
|
|
|
|
|
-
|
|
|
|
|
- // Tile output matrix to (K/NB_K, NPQ/NB_NPQ, 1) workgroups
|
|
|
|
|
- elements = { static_cast<uint32_t>(Cout), static_cast<uint32_t>(NPQ), 1 };
|
|
|
|
|
- }
|
|
|
|
|
- break;
|
|
|
|
|
|
|
+ elements = ggml_vk_get_conv_elements(dst);
|
|
|
|
|
+ } break;
|
|
|
case GGML_OP_ADD:
|
|
case GGML_OP_ADD:
|
|
|
case GGML_OP_SUB:
|
|
case GGML_OP_SUB:
|
|
|
case GGML_OP_DIV:
|
|
case GGML_OP_DIV:
|