```python
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.cpp_extension import load_inline


_cpp_source = r"""
#include <torch/extension.h>

torch::Tensor pack_depth_cuda(
    torch::Tensor x, int64_t kernel_depth, int64_t depth_padding);
torch::Tensor pack_weight_2d_cuda(torch::Tensor weight);
"""

_cuda_source = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAGuard.h>
#include <c10/cuda/CUDAException.h>
#include <cuda_runtime.h>
#include <algorithm>

__global__ void pack_depth_kernel(
    const float* __restrict__ input,
    float* __restrict__ output,
    int channels,
    int depth,
    int spatial,
    int kernel_depth,
    int depth_padding,
    int output_depth,
    int virtual_depth)
{
    __shared__ float tile[32][33];

    const int tid = threadIdx.x;
    const int row = tid >> 3;
    const int col = (tid & 7) * 4;

    const int vdepth = blockIdx.y % virtual_depth;
    const int channel_base = (blockIdx.y / virtual_depth) * 32;
    const int input_depth = vdepth - depth_padding;
    const int spatial_base = blockIdx.x * 32;
    const int batch = blockIdx.z;

    float4 value = make_float4(0.f, 0.f, 0.f, 0.f);
    if (input_depth >= 0 && input_depth < depth) {
        const int64_t offset =
            ((int64_t(batch) * channels + channel_base + row) * depth
             + input_depth) * spatial + spatial_base + col;
        value = *reinterpret_cast<const float4*>(input + offset);
    }

    tile[row][col] = value.x;
    tile[row][col + 1] = value.y;
    tile[row][col + 2] = value.z;
    tile[row][col + 3] = value.w;
    __syncthreads();

    const float4 result = make_float4(
        tile[col][row],
        tile[col + 1][row],
        tile[col + 2][row],
        tile[col + 3][row]);

    const int packed_channels = kernel_depth * channels;

    for (int k = 0; k < kernel_depth; ++k) {
        const int od = vdepth - k;
        if (od >= 0 && od < output_depth) {
            const int64_t offset =
                ((int64_t(batch) * output_depth + od) * spatial
                 + spatial_base + row) * packed_channels
                + k * channels + channel_base + col;
            *reinterpret_cast<float4*>(output + offset) = result;
        }
    }
}

__global__ void pack_weight_2d_kernel(
    const float* __restrict__ weight,
    float* __restrict__ packed,
    int64_t count,
    int input_channels,
    int output_channels,
    int kernel_depth,
    int kernel_spatial)
{
    for (int64_t i = int64_t(blockIdx.x) * blockDim.x + threadIdx.x;
         i < count;
         i += int64_t(blockDim.x) * gridDim.x) {
        int64_t q = i;
        const int ic = q % input_channels;
        q /= input_channels;
        const int kd = q % kernel_depth;
        q /= kernel_depth;
        const int spatial = q % kernel_spatial;
        const int oc = q / kernel_spatial;

        const int64_t source =
            ((int64_t(ic) * output_channels + oc) * kernel_depth
             + kernel_depth - 1 - kd) * kernel_spatial
            + kernel_spatial - 1 - spatial;

        packed[i] = weight[source];
    }
}

torch::Tensor pack_depth_cuda(
    torch::Tensor x, int64_t kernel_depth, int64_t depth_padding)
{
    TORCH_CHECK(x.is_cuda() && x.dim() == 5 &&
                x.scalar_type() == at::kFloat,
                "Expected a five-dimensional float32 CUDA tensor");
    const c10::cuda::CUDAGuard guard(x.device());
    x = x.contiguous();

    const int64_t batch = x.size(0);
    const int64_t channels = x.size(1);
    const int64_t depth = x.size(2);
    const int64_t height = x.size(3);
    const int64_t width = x.size(4);
    const int64_t spatial = height * width;
    const int64_t output_depth =
        depth + 2 * depth_padding - kernel_depth + 1;
    const int64_t virtual_depth = output_depth + kernel_depth - 1;

    TORCH_CHECK(channels % 32 == 0 && spatial % 32 == 0,
                "Unsupported packing dimensions");
    TORCH_CHECK(output_depth > 0 && batch > 0 &&
                batch <= 65535 &&
                virtual_depth * (channels / 32) <= 65535,
                "Unsupported packing grid");

    auto packed = at::empty(
        {batch * output_depth, kernel_depth * channels, height, width},
        x.options(), at::MemoryFormat::ChannelsLast);

    const dim3 grid(
        spatial / 32,
        virtual_depth * (channels / 32),
        batch);

    pack_depth_kernel<<<grid, 256, 0, at::cuda::getCurrentCUDAStream()>>>(
        x.data_ptr<float>(),
        packed.data_ptr<float>(),
        channels,
        depth,
        spatial,
        kernel_depth,
        depth_padding,
        output_depth,
        virtual_depth);

    C10_CUDA_KERNEL_LAUNCH_CHECK();
    return packed;
}

torch::Tensor pack_weight_2d_cuda(torch::Tensor weight)
{
    TORCH_CHECK(weight.is_cuda() && weight.dim() == 5 &&
                weight.scalar_type() == at::kFloat,
                "Expected five-dimensional float32 CUDA weights");
    const c10::cuda::CUDAGuard guard(weight.device());
    weight = weight.contiguous();

    const int64_t input_channels = weight.size(0);
    const int64_t output_channels = weight.size(1);
    const int64_t kd = weight.size(2);
    const int64_t kh = weight.size(3);
    const int64_t kw = weight.size(4);

    auto packed = at::empty(
        {output_channels, kd * input_channels, kh, kw},
        weight.options(), at::MemoryFormat::ChannelsLast);

    const int64_t count = weight.numel();
    if (count > 0) {
        const int blocks = static_cast<int>(
            std::min<int64_t>((count + 255) / 256, 65535));

        pack_weight_2d_kernel<<<
            blocks, 256, 0, at::cuda::getCurrentCUDAStream()>>>(
                weight.data_ptr<float>(),
                packed.data_ptr<float>(),
                count,
                input_channels,
                output_channels,
                kd,
                kh * kw);

        C10_CUDA_KERNEL_LAUNCH_CHECK();
    }
    return packed;
}
"""

_extension = load_inline(
    name="transpose3d_depth_to_conv2d_v5",
    cpp_sources=_cpp_source,
    cuda_sources=_cuda_source,
    functions=["pack_depth_cuda", "pack_weight_2d_cuda"],
    extra_cflags=["-O3"],
    extra_cuda_cflags=["-O3"],
    verbose=False,
)


class _PackDepth(torch.autograd.Function):
    @staticmethod
    def forward(ctx, x, kernel_depth, depth_padding):
        ctx.input_shape = tuple(x.shape)
        ctx.kernel_depth = kernel_depth
        ctx.depth_padding = depth_padding
        return _extension.pack_depth_cuda(x, kernel_depth, depth_padding)

    @staticmethod
    def backward(ctx, grad_output):
        n, channels, depth, height, width = ctx.input_shape
        kd = ctx.kernel_depth
        padding = ctx.depth_padding
        output_depth = depth + 2 * padding - kd + 1

        grad = grad_output.reshape(
            n, output_depth, kd * channels, height, width
        )
        grad_input = grad_output.new_zeros(ctx.input_shape)

        for k in range(kd):
            first = max(0, padding - k)
            last = min(output_depth, depth + padding - k)
            if first < last:
                input_first = first - padding + k
                input_last = last - padding + k
                contribution = grad[
                    :, first:last, k * channels:(k + 1) * channels
                ].permute(0, 2, 1, 3, 4)
                grad_input[:, :, input_first:input_last].add_(contribution)

        return grad_input, None, None


class _PackWeight2D(torch.autograd.Function):
    @staticmethod
    def forward(ctx, weight):
        ctx.original_shape = tuple(weight.shape)
        return _extension.pack_weight_2d_cuda(weight)

    @staticmethod
    def backward(ctx, grad_output):
        cin, cout, kd, kh, kw = ctx.original_shape
        grad_weight = (
            grad_output.reshape(cout, kd, cin, kh, kw)
            .permute(2, 0, 1, 3, 4)
            .flip((2, 3, 4))
            .contiguous()
        )
        return grad_weight


class ModelNew(nn.Module):
    def __init__(
        self,
        in_channels: int,
        out_channels: int,
        kernel_size: tuple,
        stride: tuple = (1, 1, 1),
        padding: tuple = (0, 0, 0),
        output_padding: tuple = (0, 0, 0),
        groups: int = 1,
        bias: bool = False,
    ):
        super().__init__()
        self.conv_transpose3d = nn.ConvTranspose3d(
            in_channels,
            out_channels,
            kernel_size,
            stride=stride,
            padding=padding,
            output_padding=output_padding,
            groups=groups,
            bias=bias,
        )

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        conv = self.conv_transpose3d

        if (
            not x.is_cuda
            or x.ndim != 5
            or x.dtype != torch.float32
            or conv.weight.dtype != torch.float32
            or x.device != conv.weight.device
            or conv.groups != 1
            or conv.stride != (1, 1, 1)
            or conv.dilation != (1, 1, 1)
            or conv.output_padding != (0, 0, 0)
            or x.shape[1] != conv.in_channels
            or x.shape[1] % 32 != 0
            or x.numel() == 0
            or x.shape[0] > 65535
            or (x.shape[3] * x.shape[4]) % 32 != 0
        ):
            return conv(x)

        kd, kh, kw = conv.kernel_size
        pd, ph, pw = (
            k - 1 - p for k, p in zip(conv.kernel_size, conv.padding)
        )
        n, channels, depth, height, width = x.shape
        output_depth = depth + 2 * pd - kd + 1
        output_height = height + 2 * ph - kh + 1
        output_width = width + 2 * pw - kw + 1
        virtual_depth = output_depth + kd - 1

        if (
            min(pd, ph, pw) < 0
            or min(output_depth, output_height, output_width) <= 0
            or virtual_depth * (channels // 32) > 65535
        ):
            return conv(x)

        packed_x = _PackDepth.apply(x, kd, pd)
        packed_weight = _PackWeight2D.apply(conv.weight)

        with torch.backends.cudnn.flags(
            enabled=torch.backends.cudnn.enabled,
            benchmark=True,
            deterministic=torch.backends.cudnn.deterministic,
            allow_tf32=torch.backends.cudnn.allow_tf32,
        ):
            output = F.conv2d(
                packed_x,
                packed_weight,
                conv.bias,
                stride=1,
                padding=(ph, pw),
            )

        return output.reshape(
            n, output_depth, conv.out_channels, output_height, output_width
        ).permute(0, 2, 1, 3, 4)
```