// Copyright © Advanced Micro Devices, Inc., or its affiliates. // SPDX-License-Identifier: MIT #pragma once #include "ConvShapeCase.hpp" #include #include #include #include #include #include #include #include #include // ============================================================================ // Shared infrastructure for forward convolution GPU-vs-CPU reference tests. // ============================================================================ namespace gpu_conv_fwd_ref_test { using namespace hipdnn_data_sdk::utilities; using namespace hipdnn_data_sdk::types; using namespace hipdnn_test_sdk::utilities; using namespace hipdnn_gpu_ref; using gpu_conv_ref_test::assertAllClose; using gpu_conv_ref_test::ConvShapeCase; using ConvFwdShapeCase = ConvShapeCase; // Core helper: fills tensors, runs GPU and CPU convolution, compares results. // Separate template params support mixed input/weight types (WDataType != XDataType). template void compareGpuVsCpuConvFwd(Tensor& xTensor, Tensor& wTensor, Tensor& yCpu, Tensor& yGpu, const std::vector& strides, const std::vector& dilations, const std::vector& prePadding, const std::vector& postPadding, float tolerance, float fillRange) { const unsigned int seed = 42; xTensor.fillWithRandomValues( static_cast(-fillRange), static_cast(fillRange), seed); wTensor.fillWithRandomValues( static_cast(-fillRange), static_cast(fillRange), seed + 1); CpuFpReferenceConvolution::fprop( xTensor, wTensor, yCpu, strides, dilations, prePadding, postPadding); GpuFpReferenceConvolution::fprop( xTensor, wTensor, yGpu, strides, dilations, prePadding, postPadding); assertAllClose(yCpu, yGpu, tolerance); } // --- Forward convolution helper overloads --- // fillRange controls the magnitude of random fill values [-fillRange, +fillRange]. // For small output types (e.g. fp8), reduce fillRange to prevent overflow: // each output element accumulates cPerGroup * Kh * Kw products, so // max output ~ numMACs * fillRange^2. Keep numMACs * fillRange^2 < type max. // Asymmetric padding with optional layout. // When layout is non-null, input/output tensors use channel-last strides (e.g. NHWC, NDHWC) // generated via Tensor(dims, layout). Weights always use default packed (KCRS) strides. // When layout is null, all tensors use default packed strides (NCHW/NCDHW). template void runGpuVsCpuConvFwd(const std::vector& xDims, const std::vector& wDims, const std::vector& yDims, const std::vector& strides, const std::vector& dilations, const std::vector& prePadding, const std::vector& postPadding, float tolerance, const TensorLayout* layout = nullptr, float fillRange = 1.0f) { auto xTensor = layout != nullptr ? Tensor(xDims, *layout) : Tensor(xDims); auto wTensor = Tensor(wDims); auto yCpu = layout != nullptr ? Tensor(yDims, *layout) : Tensor(yDims); auto yGpu = layout != nullptr ? Tensor(yDims, *layout) : Tensor(yDims); compareGpuVsCpuConvFwd(xTensor, wTensor, yCpu, yGpu, strides, dilations, prePadding, postPadding, tolerance, fillRange); } // ============================================================================ // ConvFwdShapeSuite — parameterized fixture for shape-based GPU-vs-CPU tests // ============================================================================ template class ConvFwdShapeSuite : public ::testing::TestWithParam { protected: static float tolerance(const ConvFwdShapeCase& tc) { constexpr double FILL_RANGE = 1.0; return hipdnn_test_sdk::utilities::conv:: calculateConvFpropTolerance( -FILL_RANGE, FILL_RANGE, -FILL_RANGE, FILL_RANGE, tc.wDims); } void runConvFwdShapeTest() { SKIP_IF_NO_DEVICES(); const auto& tc = GetParam(); auto yDims = tc.computeOutputDims(); runGpuVsCpuConvFwd(tc.xDims, tc.wDims, yDims, tc.strides, tc.dilations, tc.padding, tc.padding, tolerance(tc), tc.layout); } }; } // namespace gpu_conv_fwd_ref_test