#include "engine/framework/core/backend.h" #include "engine/framework/codecs/wan_video_vae_runtime.h" #include "engine/framework/debug/profiler.h" #include "engine/framework/modules/conv_modules.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { void check(bool condition, const char * message) { if (!condition) throw std::runtime_error(message); } void run_case(ggml_backend_t backend, engine::modules::Conv3dConfig config, int batch, int depth, int height, int width, bool framework, bool f32_input, bool expect_unavailable, bool check_cast = false, bool packed_weights = false, bool sample_scalar_reference = false) { namespace core = engine::core; std::unique_ptr ctx( ggml_init({4 * 1024 * 1024, nullptr, true}), ggml_free); check(bool(ctx), "context allocation failed"); core::ModuleBuildContext build{ctx.get(), "test.cudnn_conv3d", core::BackendType::Cuda}; auto x = core::make_tensor(build, f32_input ? GGML_TYPE_F32 : GGML_TYPE_F16, core::TensorShape::from_dims({batch * config.in_channels, depth, height, width})); auto w = core::make_tensor(build, GGML_TYPE_F16, core::TensorShape::from_dims( {config.out_channels * config.in_channels, config.kernel_depth, config.kernel_height, config.kernel_width})); auto bias = core::make_tensor(build, GGML_TYPE_F32, core::TensorShape::from_dims({config.out_channels})); auto reference_w = packed_weights ? core::make_tensor(build, GGML_TYPE_F16, w.shape) : w; if (packed_weights) ggml_set_input(reference_w.tensor); ggml_set_input(x.tensor); ggml_set_input(w.tensor); ggml_set_input(bias.tensor); const ggml_type output_type = config.cuda_f16_output ? GGML_TYPE_F16 : GGML_TYPE_F32; ggml_tensor * output; if (framework) { config.cuda_cudnn = true; config.cuda_prepacked_weights = packed_weights; output = engine::modules::Conv3dModule(config).build(build, x, {w, config.use_bias ? std::optional(bias) : std::nullopt}).tensor; } else { output = (config.cuda_f16_output ? ggml_conv_3d_direct_f16 : ggml_conv_3d_direct)(ctx.get(), w.tensor, x.tensor, config.stride_width, config.stride_height, config.stride_depth, config.padding_width, config.padding_height, config.padding_depth, config.dilation_width, config.dilation_height, config.dilation_depth, int(config.in_channels), batch, int(config.out_channels)); check(output->type == output_type && output->op_params[12] == int(config.cuda_f16_output), "direct Conv3D output type/default marker changed"); const auto marker = output->op_params[12]; output->op_params[12] = 0; check(!ggml_backend_supports_op(backend, output), "unmarked direct Conv3D advertised on CUDA"); output->op_params[12] = marker; ggml_conv_3d_direct_set_cudnn(output, config.use_bias ? bias.tensor : nullptr, config.cuda_bf16_round, packed_weights, config.cuda_replicate_depth_left); } check(output->type == output_type && ggml_is_contiguous(output) && output->nb[0] == ggml_type_size(output_type) && ggml_nbytes(output) == size_t(ggml_nelements(output)) * ggml_type_size(output_type), "cuDNN Conv3D output dtype/byte size mismatch"); check(output->op == GGML_OP_CONV_3D, "native output epilogue added a separate graph operation"); // Independent GPU oracle: identical convolution with standalone F32 bias // addition and ROUND_BF16. Keep its output live alongside the fused result. ggml_tensor * reference = nullptr; if (config.use_bias || config.cuda_bf16_round || config.cuda_f16_output || packed_weights) { reference = ggml_conv_3d_direct(ctx.get(), reference_w.tensor, x.tensor, config.stride_width, config.stride_height, config.stride_depth, config.padding_width, config.padding_height, config.padding_depth, config.dilation_width, config.dilation_height, config.dilation_depth, int(config.in_channels), batch, int(config.out_channels)); ggml_conv_3d_direct_set_cudnn(reference, nullptr, false, false, config.cuda_replicate_depth_left); if (config.use_bias) { reference = ggml_add(ctx.get(), reference, ggml_reshape_4d(ctx.get(), bias.tensor, 1, 1, 1, config.out_channels)); } if (config.cuda_bf16_round) reference = ggml_round_bf16(ctx.get(), reference); if (config.cuda_f16_output) reference = ggml_cast(ctx.get(), reference, GGML_TYPE_F16); ggml_set_output(reference); } ggml_set_output(output); auto * graph = ggml_new_graph_custom(ctx.get(), 128, false); ggml_build_forward_expand(graph, output); if (reference) ggml_build_forward_expand(graph, reference); ggml_tensor * conv = nullptr; for (int i = 0; i < ggml_graph_n_nodes(graph); ++i) { auto * node = ggml_graph_node(graph, i); if (node->op == GGML_OP_CONV_3D && node == output) conv = node; } check(conv && conv->op_params[12] == 1, "framework did not select marked direct Conv3D"); check(conv->src[1] == x.tensor, "marked Conv3D introduced a separate activation cast/copy"); check(conv->src[2] == (config.use_bias ? bias.tensor : nullptr), "incorrect fused bias dependency"); check(conv->op_params[13] == int(config.cuda_bf16_round), "incorrect fused BF16 round flag"); check(conv->op_params[14] == int(packed_weights), "incorrect explicit packed-weight flag"); conv->op_params[14] = 2; check(!ggml_backend_supports_op(backend, conv), "unknown packed-weight flag accepted"); conv->op_params[14] = int(packed_weights); check(conv->op_params[15] == config.cuda_replicate_depth_left + 1, "incorrect replicated depth padding"); conv->op_params[15] = 2 * config.padding_depth + 2; check(!ggml_backend_supports_op(backend, conv), "excess replicated depth padding accepted"); conv->op_params[15] = config.cuda_replicate_depth_left + 1; check(ggml_backend_supports_op(backend, conv) != expect_unavailable, "cuDNN Conv3D support does not match the requested build configuration"); // Reject CPU/Metal scheduling without initializing or executing either backend. for (size_t i = 0; i < ggml_backend_dev_count(); ++i) { const auto dev = ggml_backend_dev_get(i); const std::string name = ggml_backend_reg_name(ggml_backend_dev_backend_reg(dev)); if (name == "CPU" || name == "Metal") { check(!ggml_backend_dev_supports_op(dev, conv), "CUDA marker accepted by CPU/Metal"); } } conv->op_params[12] = 2; check(!ggml_backend_supports_op(backend, conv), "unknown lowering marker accepted"); conv->op_params[12] = 1; conv->op_params[13] = 2; check(!ggml_backend_supports_op(backend, conv), "unknown BF16 round flag accepted"); conv->op_params[13] = int(config.cuda_bf16_round); if (config.cuda_f16_output) { conv->op_params[13] = 1; check(!ggml_backend_supports_op(backend, conv), "F16 output with BF16 rounding advertised"); conv->op_params[13] = 0; } auto * saved_bias = conv->src[2]; conv->src[2] = bias.tensor; bias.tensor->type = GGML_TYPE_F16; check(!ggml_backend_supports_op(backend, conv), "non-F32 bias advertised"); bias.tensor->type = GGML_TYPE_F32; ++bias.tensor->ne[0]; check(!ggml_backend_supports_op(backend, conv), "incorrect bias channel count advertised"); --bias.tensor->ne[0]; if (config.out_channels > 1) { bias.tensor->nb[0] += sizeof(float); check(!ggml_backend_supports_op(backend, conv), "noncontiguous bias advertised"); bias.tensor->nb[0] -= sizeof(float); } conv->src[2] = saved_bias; conv->src[3] = bias.tensor; check(!ggml_backend_supports_op(backend, conv), "unexpected extra source advertised"); conv->src[3] = nullptr; const auto saved_type = conv->src[1]->type; conv->src[1]->type = GGML_TYPE_BF16; check(!ggml_backend_supports_op(backend, conv), "unsupported BF16 input advertised"); conv->src[1]->type = saved_type; for (int axis = 0; axis < 4; ++axis) { if (conv->src[0]->ne[axis] == 1) continue; const auto saved_stride = conv->src[0]->nb[axis]; conv->src[0]->nb[axis] += sizeof(ggml_fp16_t); check(!ggml_backend_supports_op(backend, conv), "noncontiguous weights advertised"); conv->src[0]->nb[axis] = saved_stride; break; } if (expect_unavailable) return; core::validate_backend_graph_supported(backend, graph, "test.cudnn_conv3d"); std::unique_ptr arena( ggml_gallocr_new(ggml_backend_get_default_buffer_type(backend)), ggml_gallocr_free); check(bool(arena) && ggml_gallocr_alloc_graph(arena.get(), graph), "graph allocation failed"); // Release captured backend resources before arena/context destruction, also on failure. const auto release = [backend](ggml_cgraph * g) { core::release_backend_graph_resources(backend, g, true); }; std::unique_ptr graph_resources(graph, release); std::vector xf(size_t(ggml_nelements(x.tensor))), wf(size_t(ggml_nelements(w.tensor))); std::vector bf(size_t(config.out_channels)); std::vector xh(xf.size()), wh(wf.size()); // Include non-F16 values and rounding ties. The scalar reference uses exactly // the values of the old explicit F32 -> F16 activation cast. const float ties[] = {1.00048828125f, 1.00146484375f, -1.00048828125f, -1.00146484375f}; for (size_t i = 0; i < xf.size(); ++i) { xf[i] = float(int(i % 17) - 8) / 16 + float(int(i % 5) - 2) * 0.00013f; if (i < 4) xf[i] = ties[i]; xh[i] = ggml_fp32_to_fp16(xf[i]); } for (size_t i = 0; i < wf.size(); ++i) { wf[i] = float(int(i % 13) - 6) / 32; wh[i] = ggml_fp32_to_fp16(wf[i]); } if (check_cast) { check(config.in_channels == 1 && config.out_channels == 1 && config.kernel_depth == 1 && config.kernel_height == 1 && config.kernel_width == 1 && config.stride_depth == 1 && config.stride_height == 1 && config.stride_width == 1 && config.padding_depth == 0 && config.padding_height == 0 && config.padding_width == 0, "cast-value check requires identity convolution"); wf[0] = 1.0f; wh[0] = ggml_fp32_to_fp16(1.0f); } for (size_t i = 0; i < bf.size(); ++i) { bf[i] = float(i + 1) / 128; if (config.cuda_bf16_round || config.cuda_f16_output || check_cast) bf[i] += float(i + 1) * 0.00013f; } const int od = int(output->ne[2]), oh = int(output->ne[1]), ow = int(output->ne[0]); check(od == (depth + 2 * config.padding_depth - config.dilation_depth * (config.kernel_depth - 1) - 1) / config.stride_depth + 1 && oh == (height + 2 * config.padding_height - config.dilation_height * (config.kernel_height - 1) - 1) / config.stride_height + 1 && ow == (width + 2 * config.padding_width - config.dilation_width * (config.kernel_width - 1) - 1) / config.stride_width + 1 && output->ne[3] == batch * config.out_channels, "incorrect Conv3D output shape"); std::vector expected(size_t(ggml_nelements(output))), previous; if (sample_scalar_reference) std::fill(expected.begin(), expected.end(), std::numeric_limits::quiet_NaN()); std::vector previous_half; for (int n = 0; n < batch; ++n) for (int oc = 0; oc < config.out_channels; ++oc) for (int z = 0; z < od; ++z) for (int y = 0; y < oh; ++y) for (int col = 0; col < ow; ++col) { // Keep the large workspace-budget case cheap: cover both batches, // channel ends, spatial borders, and the partial final depth tile. if (sample_scalar_reference && ((oc != 0 && oc != config.out_channels / 2 && oc != config.out_channels - 1) || (z != 0 && z != od / 2 && z != od - 1) || (y != 0 && y != oh / 2 && y != oh - 1) || (col != 0 && col != ow / 2 && col != ow - 1))) continue; float sum = 0; for (int ic = 0; ic < config.in_channels; ++ic) for (int kz = 0; kz < config.kernel_depth; ++kz) for (int ky = 0; ky < config.kernel_height; ++ky) for (int kx = 0; kx < config.kernel_width; ++kx) { const int depth_left = config.cuda_replicate_depth_left >= 0 ? config.cuda_replicate_depth_left : config.padding_depth; int iz = z * config.stride_depth - depth_left + kz * config.dilation_depth; if (config.cuda_replicate_depth_left >= 0) iz = std::clamp(iz, 0, depth - 1); const int iy = y * config.stride_height - config.padding_height + ky * config.dilation_height; const int ix = col * config.stride_width - config.padding_width + kx * config.dilation_width; if (iz < 0 || iz >= depth || iy < 0 || iy >= height || ix < 0 || ix >= width) continue; const size_t xi = (((n * config.in_channels + ic) * depth + iz) * height + iy) * width + ix; const size_t wi = (((oc * config.in_channels + ic) * config.kernel_depth + kz) * config.kernel_height + ky) * config.kernel_width + kx; sum += ggml_fp16_to_fp32(xh[xi]) * wf[wi]; } const size_t oi = (((n * config.out_channels + oc) * od + z) * oh + y) * ow + col; expected[oi] = ggml_fp16_to_fp32(ggml_fp32_to_fp16(sum)) + (config.use_bias ? bf[oc] : 0); if (config.cuda_bf16_round) { expected[oi] = ggml_bf16_to_fp32(ggml_fp32_to_bf16(expected[oi])); } if (config.cuda_f16_output) { expected[oi] = ggml_fp16_to_fp32(ggml_fp32_to_fp16(expected[oi])); } } std::vector packed(wh.size()); if (packed_weights) { const size_t kernel = size_t(config.kernel_depth) * config.kernel_height * config.kernel_width; for (size_t oc = 0; oc < size_t(config.out_channels); ++oc) for (size_t k = 0; k < kernel; ++k) for (size_t ic = 0; ic < size_t(config.in_channels); ++ic) packed[(oc * kernel + k) * config.in_channels + ic] = wh[(oc * config.in_channels + ic) * kernel + k]; } for (int repeat = 0; repeat < 3; ++repeat) { ggml_backend_tensor_set(x.tensor, f32_input ? static_cast(xf.data()) : xh.data(), 0, f32_input ? xf.size() * sizeof(float) : xh.size() * sizeof(ggml_fp16_t)); ggml_backend_tensor_set(w.tensor, packed_weights ? packed.data() : wh.data(), 0, wh.size() * sizeof(ggml_fp16_t)); if (packed_weights) ggml_backend_tensor_set(reference_w.tensor, wh.data(), 0, wh.size() * sizeof(ggml_fp16_t)); if (config.use_bias) ggml_backend_tensor_set(bias.tensor, bf.data(), 0, bf.size() * sizeof(float)); check(ggml_backend_graph_compute(backend, graph) == GGML_STATUS_SUCCESS, "CUDA execution failed"); ggml_backend_synchronize(backend); std::vector actual(expected.size()); std::vector actual_half; if (config.cuda_f16_output) { actual_half.resize(actual.size()); ggml_backend_tensor_get(output, actual_half.data(), 0, actual_half.size() * sizeof(ggml_fp16_t)); for (size_t i = 0; i < actual.size(); ++i) actual[i] = ggml_fp16_to_fp32(actual_half[i]); } else { ggml_backend_tensor_get(output, actual.data(), 0, actual.size() * sizeof(float)); } if (reference) { if (config.cuda_f16_output) { std::vector separate(actual_half.size()); ggml_backend_tensor_get(reference, separate.data(), 0, separate.size() * sizeof(ggml_fp16_t)); check(std::memcmp(actual_half.data(), separate.data(), actual_half.size() * sizeof(ggml_fp16_t)) == 0, "F16 output differs bitwise from F32 convolution + F32 bias -> F16 GPU oracle"); } else { std::vector separate(actual.size()); ggml_backend_tensor_get(reference, separate.data(), 0, separate.size() * sizeof(float)); check(std::memcmp(actual.data(), separate.data(), actual.size() * sizeof(float)) == 0, "fused epilogue differs bitwise from standalone GPU bias/ROUND_BF16"); } } for (size_t i = 0; i < actual.size(); ++i) { check(std::isfinite(actual[i]), "nonfinite Conv3D output"); if (sample_scalar_reference && !std::isfinite(expected[i])) continue; if (check_cast) check(actual[i] == expected[i], "identity convolution changed cast/bias/round values"); const float relative_tolerance = config.cuda_bf16_round ? 0.01f : 0.001f; check(std::isfinite(actual[i]) && std::abs(actual[i] - expected[i]) <= 0.002f + relative_tolerance * std::abs(expected[i]), "cuDNN Conv3D differs from scalar cross-correlation"); } if (repeat != 0) check(actual == previous, "repeated/captured Conv3D changed output"); if (repeat != 0 && config.cuda_f16_output) { check(actual_half == previous_half, "repeated/captured F16 output changed bits"); } previous_half = std::move(actual_half); previous = std::move(actual); } } // Storage oracle: the existing F32 fused kernel followed by an independent GPU // cast. Exercise both reduction kernels and their channel/spatial tail handling. void run_norm_case(ggml_backend_t backend, int channels, bool f32_input, bool coalesced, bool conv_available) { namespace core = engine::core; namespace codecs = engine::codecs; std::unique_ptr ctx( ggml_init({4 * 1024 * 1024, nullptr, true}), ggml_free); check(bool(ctx), "norm context allocation failed"); core::ModuleBuildContext build{ctx.get(), "test.channel_rms_silu", core::BackendType::Cuda}; auto x = core::make_tensor(build, f32_input ? GGML_TYPE_F32 : GGML_TYPE_F16, core::TensorShape::from_dims({channels, 2, 3, 17})); auto gamma = core::make_tensor(build, GGML_TYPE_F32, core::TensorShape::from_dims({channels})); ggml_set_input(x.tensor); ggml_set_input(gamma.tensor); codecs::WanVideoRMSNorm3dConfig config; config.channels = channels; config.eps = 1.e-8f; config.cuda_fast_lowering = true; config.fuse_silu = true; config.cuda_f16_output = true; auto fused = codecs::WanVideoRMSNorm3dModule(config).build(build, x, {gamma}); config.cuda_f16_output = false; auto f32 = codecs::WanVideoRMSNorm3dModule(config).build(build, x, {gamma}); check(fused.type == GGML_TYPE_F16 && f32.type == GGML_TYPE_F32 && fused.tensor->op == GGML_OP_RMS_NORM_CHANNELS_SILU && fused.tensor->src[0] == x.tensor && f32.tensor->src[0] == x.tensor, "fused module inserted an input cast or selected the wrong operation/type"); check(fused.tensor->op_params[1] == GGML_RMS_NORM_CHANNELS_LOWERING_CUDA_COALESCED && f32.tensor->op_params[1] == GGML_RMS_NORM_CHANNELS_LOWERING_CUDA_COALESCED, "fused module did not select the established CUDA coalesced lowering"); if (!coalesced) { // Factory default (zero) selects the original block reduction. fused.tensor->op_params[1] = 0; f32.tensor->op_params[1] = 0; } auto * oracle = ggml_cast(ctx.get(), f32.tensor, GGML_TYPE_F16); const size_t count = size_t(ggml_nelements(x.tensor)); check(ggml_is_contiguous(fused.tensor) && ggml_nbytes(fused.tensor) == count * sizeof(ggml_fp16_t), "F16 norm output byte layout mismatch"); check(ggml_backend_supports_op(backend, fused.tensor), "CUDA rejected F16 RMSNorm/SiLU"); for (size_t i = 0; i < ggml_backend_dev_count(); ++i) { auto dev = ggml_backend_dev_get(i); const std::string name = ggml_backend_reg_name(ggml_backend_dev_backend_reg(dev)); if (name == "CPU" || name == "Metal" || name == "Vulkan") { check(!ggml_backend_dev_supports_op(dev, fused.tensor), "unsupported backend advertised F16 RMSNorm/SiLU"); } } // Explicit F16 requests must fail rather than becoming an implicit cast path. for (int invalid = 0; invalid < 3; ++invalid) { auto bad = config; bad.cuda_f16_output = true; if (invalid == 0) bad.cuda_fast_lowering = false; if (invalid == 1) bad.fuse_silu = false; auto non_cuda = build; non_cuda.backend_type = core::BackendType::Cpu; bool rejected = false; try { codecs::WanVideoRMSNorm3dModule module(bad); if (invalid == 2) module.build(non_cuda, x, {gamma}); } catch (const std::runtime_error &) { rejected = true; } check(rejected, "invalid F16 norm configuration silently accepted"); } auto non_cuda = build; non_cuda.backend_type = core::BackendType::Cpu; auto fallback = codecs::WanVideoRMSNorm3dModule(config).build(non_cuda, x, {gamma}); check(fallback.type == GGML_TYPE_F32 && fallback.tensor->op == GGML_OP_UNARY && ggml_get_unary_op(fallback.tensor) == GGML_UNARY_OP_SILU, "generic fused-SiLU request did not use the existing module composition"); auto * graph = ggml_new_graph_custom(ctx.get(), 128, false); ggml_set_output(fused.tensor); ggml_set_output(oracle); ggml_build_forward_expand(graph, fused.tensor); ggml_build_forward_expand(graph, oracle); ggml_tensor * conv = nullptr; ggml_tensor * conv_oracle = nullptr; ggml_tensor * weights = nullptr; if (conv_available) { weights = ggml_new_tensor_4d(ctx.get(), GGML_TYPE_F16, 1, 1, 1, channels * 3); ggml_set_input(weights); conv = ggml_conv_3d_direct_f16(ctx.get(), weights, fused.tensor, 1, 1, 1, 0, 0, 0, 1, 1, 1, channels, 1, 3); conv_oracle = ggml_conv_3d_direct_f16(ctx.get(), weights, oracle, 1, 1, 1, 0, 0, 0, 1, 1, 1, channels, 1, 3); ggml_conv_3d_direct_set_cudnn(conv); ggml_conv_3d_direct_set_cudnn(conv_oracle); check(conv->src[1] == fused.tensor, "norm-to-conv inserted a conversion"); ggml_set_output(conv); ggml_set_output(conv_oracle); ggml_build_forward_expand(graph, conv); ggml_build_forward_expand(graph, conv_oracle); } core::validate_backend_graph_supported(backend, graph, "test.channel_rms_silu"); std::unique_ptr arena( ggml_gallocr_new(ggml_backend_get_default_buffer_type(backend)), ggml_gallocr_free); check(bool(arena) && ggml_gallocr_alloc_graph(arena.get(), graph), "norm graph allocation failed"); const auto release = [backend](ggml_cgraph * g) { core::release_backend_graph_resources(backend, g, true); }; std::unique_ptr graph_resources(graph, release); std::vector xf(count), gf(channels); std::vector xh(count), actual(count), expected(count), previous; std::vector wh(size_t(channels) * 3, ggml_fp32_to_fp16(0.f)); for (int oc = 0; oc < 3; ++oc) wh[size_t(oc) * channels + oc % channels] = ggml_fp32_to_fp16(1.f); if (weights) ggml_backend_tensor_set(weights, wh.data(), 0, wh.size() * sizeof(ggml_fp16_t)); for (int repeat = 0; repeat < 6; ++repeat) { // Change data after capture, then verify repeated replay is stable too. const int revision = repeat / 3; for (size_t i = 0; i < count; ++i) { xf[i] = float(int((i * 7 + revision) % 41) - 20) * 0.07137f; if (i % 102 == 0) xf[i] = 0.f; xh[i] = ggml_fp32_to_fp16(xf[i]); } for (int c = 0; c < channels; ++c) gf[c] = float(c % 11 - 5) * 0.1937f + revision * 0.017f; ggml_backend_tensor_set(x.tensor, f32_input ? static_cast(xf.data()) : xh.data(), 0, count * (f32_input ? sizeof(float) : sizeof(ggml_fp16_t))); ggml_backend_tensor_set(gamma.tensor, gf.data(), 0, gf.size() * sizeof(float)); check(ggml_backend_graph_compute(backend, graph) == GGML_STATUS_SUCCESS, "norm CUDA execution failed"); ggml_backend_synchronize(backend); ggml_backend_tensor_get(fused.tensor, actual.data(), 0, actual.size() * sizeof(ggml_fp16_t)); ggml_backend_tensor_get(oracle, expected.data(), 0, expected.size() * sizeof(ggml_fp16_t)); check(actual == expected, "F16 RMSNorm/SiLU differs bitwise from F32 fused kernel + GPU cast"); for (auto value : actual) check(std::isfinite(ggml_fp16_to_fp32(value)), "nonfinite norm result"); if (repeat % 3 != 0) check(actual == previous, "repeated/captured F16 norm changed bits"); if (repeat == 3) check(actual != previous, "captured norm ignored updated input/gamma"); previous = actual; if (conv) { std::vector got(size_t(ggml_nelements(conv))), ref(got.size()); ggml_backend_tensor_get(conv, got.data(), 0, got.size() * sizeof(ggml_fp16_t)); ggml_backend_tensor_get(conv_oracle, ref.data(), 0, ref.size() * sizeof(ggml_fp16_t)); check(got == ref, "F16 norm-to-conv differs from independent cast-to-conv oracle"); } } } } // namespace int main(int argc, char ** argv) { if ((argc != 3 && argc != 4) || std::string(argv[1]) != "--log" || (argc == 4 && std::string(argv[3]) != "--expect-unavailable")) { std::cerr << "Usage: cuda_cudnn_conv3d_test --log PATH [--expect-unavailable]\n"; return 1; } engine::debug::configure_logging({true, argv[2]}); const auto started = std::chrono::steady_clock::now(); int result = 0; try { engine::core::ensure_backends_loaded(); auto reg = ggml_backend_reg_by_name("CUDA"); if (!reg || ggml_backend_reg_dev_count(reg) == 0) { std::cerr << "SKIP: CUDA device unavailable\n"; result = 77; } else { std::unique_ptr backend( ggml_backend_dev_init(ggml_backend_reg_dev_get(reg, 0), nullptr), ggml_backend_free); check(bool(backend), "CUDA backend initialization failed"); engine::modules::Conv3dConfig c; c.in_channels = 3; c.out_channels = 5; c.kernel_depth = 2; c.kernel_height = 3; c.kernel_width = 2; c.stride_depth = 2; c.stride_height = 1; c.stride_width = 2; c.padding_depth = 1; c.padding_height = 2; c.padding_width = 1; c.dilation_depth = 2; c.dilation_height = 1; c.dilation_width = 2; c.use_bias = false; const bool unavailable = argc == 4; for (int channels : {3, 17, 128, 512, 513}) { for (bool f32_input : {false, true}) { for (bool coalesced : {false, true}) { run_norm_case(backend.get(), channels, f32_input, coalesced, !unavailable); } } } run_case(backend.get(), c, 2, 5, 6, 7, false, false, unavailable); run_case(backend.get(), c, 2, 5, 6, 7, false, true, unavailable); c.use_bias = true; run_case(backend.get(), c, 2, 5, 6, 7, true, true, unavailable); for (int left : {0, 1, 2}) { auto padded = c; padded.cuda_replicate_depth_left = left; for (bool f32_input : {false, true}) { padded.cuda_f16_output = false; run_case(backend.get(), padded, 2, 5, 6, 7, true, f32_input, unavailable); padded.cuda_f16_output = true; run_case(backend.get(), padded, 2, 5, 6, 7, true, f32_input, unavailable, false, true); } } if (!unavailable) { auto tiled = c; tiled.in_channels = 256; tiled.out_channels = 2048; tiled.kernel_depth = tiled.kernel_height = tiled.kernel_width = 3; tiled.stride_depth = tiled.stride_height = tiled.stride_width = 1; tiled.dilation_depth = tiled.dilation_height = tiled.dilation_width = 1; tiled.padding_depth = tiled.padding_height = tiled.padding_width = 1; tiled.cuda_replicate_depth_left = 2; tiled.cuda_f16_output = true; run_case(backend.get(), tiled, 2, 9, 32, 32, true, true, false, false, true, true); } for (bool f16_output : {false, true}) { auto packed = c; packed.cuda_f16_output = f16_output; for (bool f32_input : {false, true}) { run_case(backend.get(), packed, 2, 5, 6, 7, false, f32_input, unavailable, false, true); } } auto typed = c; typed.cuda_cudnn = true; typed.cuda_f16_output = true; for (bool cudnn : {false, true}) { auto invalid_typed = typed; invalid_typed.cuda_cudnn = cudnn; invalid_typed.cuda_bf16_round = cudnn; bool rejected_typed = false; try { engine::modules::Conv3dModule module(invalid_typed); } catch (const std::runtime_error &) { rejected_typed = true; } check(rejected_typed, "invalid F16 output configuration accepted"); } typed.use_bias = false; run_case(backend.get(), typed, 2, 5, 6, 7, false, false, unavailable); typed.use_bias = true; run_case(backend.get(), typed, 2, 5, 6, 7, false, true, unavailable); run_case(backend.get(), typed, 2, 5, 6, 7, true, false, unavailable); run_case(backend.get(), typed, 2, 5, 6, 7, true, true, unavailable); auto invalid = c; invalid.cuda_bf16_round = true; bool rejected = false; try { engine::modules::Conv3dModule module(invalid); } catch (const std::runtime_error &) { rejected = true; } check(rejected, "BF16 rounding accepted without cuDNN selection"); c.cuda_bf16_round = true; run_case(backend.get(), c, 2, 5, 6, 7, true, true, unavailable); c.use_bias = false; run_case(backend.get(), c, 2, 5, 6, 7, false, false, unavailable); c.cuda_bf16_round = false; c.use_bias = true; c.kernel_depth = c.kernel_height = c.kernel_width = 1; c.stride_depth = c.stride_height = c.stride_width = 1; c.padding_depth = c.padding_height = c.padding_width = 0; c.dilation_depth = c.dilation_height = c.dilation_width = 1; run_case(backend.get(), c, 1, 3, 4, 5, true, false, unavailable); c.in_channels = c.out_channels = 1; c.use_bias = false; run_case(backend.get(), c, 2, 3, 4, 5, true, true, unavailable, true); c.use_bias = true; run_case(backend.get(), c, 2, 3, 4, 5, true, true, unavailable, true); c.cuda_bf16_round = true; run_case(backend.get(), c, 2, 3, 4, 5, true, true, unavailable, true); run_case(backend.get(), c, 2, 3, 4, 5, false, false, unavailable, true); c.use_bias = false; run_case(backend.get(), c, 2, 3, 4, 5, true, true, unavailable, true); typed = c; typed.cuda_cudnn = true; typed.cuda_bf16_round = false; typed.cuda_f16_output = true; run_case(backend.get(), typed, 2, 3, 4, 5, true, true, unavailable, true); typed.use_bias = true; run_case(backend.get(), typed, 2, 3, 4, 5, true, false, unavailable, true); std::cout << "PASS: cuDNN Conv3D contracts" << (unavailable ? " (disabled build)" : " and scalar parity") << '\n'; } } catch (const std::exception & error) { std::cerr << error.what() << '\n'; result = 1; } engine::debug::timing_log_scalar("session.wall_ms", engine::debug::elapsed_ms(started)); return result; }