void Compute(OpKernelContext* context) override {
    const Tensor& input = context->input(0);
    const Tensor& filter_sizes = context->input(1);
    OP_REQUIRES(
        context, TensorShapeUtils::IsVector(filter_sizes.shape()),
        errors::InvalidArgument(
            "Conv2DBackpropFilter: filter_sizes input must be 1-dim, not ",
            filter_sizes.dims()));
    TensorShape filter_shape;
    const int32* filter_sizes_data = filter_sizes.template flat<int32>().data();
    for (int i = 0; i < filter_sizes.NumElements(); ++i) {
      OP_REQUIRES(context, filter_sizes_data[i] >= 0,
                  errors::InvalidArgument("Dimension ", i,
                                          " of filter_sizes must be >= 0"));
      OP_REQUIRES_OK(context,
                     filter_shape.AddDimWithStatus(filter_sizes_data[i]));
    }
    const TensorShape& input_shape = input.shape();

    EXTRACT_AND_VERIFY_DIMENSIONS("DepthwiseConv2DBackpropFilter");
    Tensor* filter_backprop = nullptr;
    OP_REQUIRES_OK(context, context->forward_input_or_allocate_output(
                                {1}, 0, filter_shape, &filter_backprop));

    // If there is nothing to compute, return.
    if (out_backprop.shape().num_elements() == 0) {
      return;
    }

    // If in_depth==1, this operation is just a standard convolution.
    // Depthwise convolution is a special case of cuDNN's grouped convolution.
    bool use_cudnn = std::is_same<Device, GPUDevice>::value &&
                     (in_depth == 1 ||
                      (use_cudnn_grouped_conv_ &&
                       (IsCudnnSupportedFilterSize(/*filter_rows=*/filter_rows,
                                                   /*filter_cols=*/filter_cols,
                                                   /*in_depth=*/in_depth,
                                                   /*out_depth=*/out_depth) ||
                        OpDeterminismRequired())));

    VLOG(2) << "DepthwiseConv2dNativeBackpropFilter: "
            << " Input: [" << batch << ", " << input_rows << ", " << input_cols
            << ", " << in_depth << "]; Filter: [" << filter_rows << ", "
            << filter_cols << ", " << in_depth << ", " << depth_multiplier
            << "]; Output: [" << batch << ", " << out_rows << ", " << out_cols
            << ", " << out_depth << "], stride = " << stride_
            << ", pad_rows = " << pad_top << ", pad_cols = " << pad_left
            << ", Use cuDNN: " << use_cudnn;

    if (use_cudnn) {
      // Reshape from TF depthwise filter to cuDNN grouped convolution filter:
      //
      //                  | TensorFlow       | cuDNN
      // --------------------------------------------------------------------
      // filter_out_depth | depth_multiplier | depth_multiplier * group_count
      // filter_in_depth  | in_depth         | in_depth / group_count
      //
      // For depthwise convolution, we have group_count == in_depth.
      int32_t filter_in_depth = 1;
      TensorShape shape =
          TensorShape{filter_rows, filter_cols, filter_in_depth, out_depth};
      Tensor reshaped_filter(/*type=*/dtype_);
      OP_REQUIRES(
          context, reshaped_filter.CopyFrom(*filter_backprop, shape),
          errors::Internal(
              "Failed to reshape filter tensor for grouped convolution."));

      // TODO(yangzihao): Send in arbitrary dilation rates after the dilated
      // conv is supported.
      launcher_(context, /*use_cudnn=*/true, cudnn_use_autotune_, out_backprop,
                input,
                /*row_dilation=*/1, /*col_dilation=*/1, stride_, stride_,
                padding_, explicit_paddings_, &reshaped_filter, data_format_);
      return;
    }

    // For GPU inputs with type half, we cast inputs to float and outputs back
    // to half, as half implementation is slow and does not use full precision
    // accumulation in some cases.
    constexpr bool cast_to_float = std::is_same<T, Eigen::half>::value &&
                                   std::is_same<Device, GPUDevice>::value;
    using U = typename std::conditional<cast_to_float, float, T>::type;
    Tensor casted_out_backprop = out_backprop;
    Tensor casted_input = input;
    Tensor casted_filter_backprop = *filter_backprop;
    const Device& device = context->template eigen_device<Device>();
    if (cast_to_float) {
      functor::CastFunctor<Device, float, Eigen::half> cast;
      OP_REQUIRES_OK(context,
                     context->allocate_temp(DT_FLOAT, out_backprop.shape(),
                                            &casted_out_backprop));
      cast(device, casted_out_backprop.template flat<float>(),
           out_backprop.template flat<Eigen::half>());
      OP_REQUIRES_OK(context, context->allocate_temp(DT_FLOAT, input.shape(),
                                                     &casted_input));
      cast(device, casted_input.template flat<float>(),
           input.template flat<Eigen::half>());
      OP_REQUIRES_OK(context,
                     context->allocate_temp(DT_FLOAT, filter_backprop->shape(),
                                            &casted_filter_backprop));
    }

    auto out_backprop_ptr = casted_out_backprop.template flat<U>().data();
    auto input_ptr = casted_input.template flat<U>().data();
    auto filter_backprop_ptr = casted_filter_backprop.template flat<U>().data();
    LaunchDepthwiseConvBackpropFilterOp<Device, U>()(
        context, args, out_backprop_ptr, input_ptr, filter_backprop_ptr,
        data_format_);

    if (cast_to_float) {
      functor::CastFunctor<Device, Eigen::half, float> cast;
      const Tensor& casted_filter_backprop_const = casted_filter_backprop;
      cast(device, filter_backprop->template flat<Eigen::half>(),
           casted_filter_backprop_const.template flat<float>());
    }
  }