diff --git a/.github/workflows/linux_cuda_wheel.yaml b/.github/workflows/linux_cuda_wheel.yaml new file mode 100644 index 00000000..acc036cb --- /dev/null +++ b/.github/workflows/linux_cuda_wheel.yaml @@ -0,0 +1,144 @@ +name: Build and test Linux CUDA wheels + +on: + pull_request: + push: + branches: + - nightly + - main + - release/* + tags: + - v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+ + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}-${{ github.ref_type == 'branch' && github.sha }}-${{ github.event_name == 'workflow_dispatch' }} + cancel-in-progress: true + +permissions: + id-token: write + contents: write + +defaults: + run: + shell: bash -l -eo pipefail {0} + +jobs: + generate-matrix: + uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main + with: + package-type: wheel + os: linux + test-infra-repository: pytorch/test-infra + test-infra-ref: main + with-cpu: disable + with-xpu: disable + with-rocm: disable + with-cuda: enable + build-python-only: "disable" + build: + needs: generate-matrix + strategy: + fail-fast: false + name: Build and Upload wheel + uses: pytorch/test-infra/.github/workflows/build_wheels_linux.yml@main + with: + repository: pytorch/torchcodec + ref: "" + test-infra-repository: pytorch/test-infra + test-infra-ref: main + build-matrix: ${{ needs.generate-matrix.outputs.matrix }} + post-script: packaging/post_build_script.sh + smoke-test-script: packaging/fake_smoke_test.py + package-name: torchcodec + trigger-event: ${{ github.event_name }} + build-platform: "python-build-package" + build-command: "BUILD_AGAINST_ALL_FFMPEG_FROM_S3=1 ENABLE_CUDA=1 python -m build --wheel -vvv --no-isolation" + + install-and-test: + runs-on: linux.4xlarge.nvidia.gpu + strategy: + fail-fast: false + matrix: + # 3.9 corresponds to the minimum python version for which we build + # the wheel unless the label cliflow/binaries/all is present in the + # PR. + # For the actual release we should add that label and change this to + # include more python versions. + python-version: ['3.9'] + cuda-version: ['11.8', '12.1', '12.4'] + ffmpeg-version-for-tests: ['5', '6', '7'] + container: + image: "pytorch/manylinux-builder:cuda${{ matrix.cuda-version }}" + options: "--gpus all -e NVIDIA_DRIVER_CAPABILITIES=video,compute,utility" + if: ${{ always() }} + needs: build + steps: + - name: Setup env vars + run: | + cuda_version_without_periods=$(echo "${{ matrix.cuda-version }}" | sed 's/\.//g') + echo cuda_version_without_periods=${cuda_version_without_periods} >> $GITHUB_ENV + - uses: actions/download-artifact@v3 + with: + name: pytorch_torchcodec__3.9_cu${{ env.cuda_version_without_periods }}_x86_64 + path: pytorch/torchcodec/dist/ + - name: Setup miniconda using test-infra + uses: ahmadsharif1/test-infra/.github/actions/setup-miniconda@14bc3c29f88d13b0237ab4ddf00aa409e45ade40 + with: + python-version: ${{ matrix.python-version }} + default-packages: "conda-forge::ffmpeg=${{ matrix.ffmpeg-version-for-tests }}" + - name: Check env + run: | + ${CONDA_RUN} env + ${CONDA_RUN} conda info + ${CONDA_RUN} nvidia-smi + - name: Update pip + run: ${CONDA_RUN} python -m pip install --upgrade pip + - name: Install PyTorch + run: | + ${CONDA_RUN} python -m pip install --pre torch --index-url https://download.pytorch.org/whl/nightly/cu${{ env.cuda_version_without_periods }} + ${CONDA_RUN} python -c 'import torch; print(f"{torch.__version__}"); print(f"{torch.__file__}"); print(f"{torch.cuda.is_available()=}")' + - name: Install torchcodec from the wheel + run: | + wheel_path=`find pytorch/torchcodec/dist -type f -name "*.whl"` + echo Installing $wheel_path + ${CONDA_RUN} python -m pip install $wheel_path -vvv + + - name: Check out repo + uses: actions/checkout@v3 + + - name: Install cuda runtime dependencies + run: | + # For some reason nvidia::libnpp=12.4 doesn't install but nvidia/label/cuda-12.4.0::libnpp does. + # So we use the latter convention for libnpp. + ${CONDA_RUN} conda install --yes nvidia/label/cuda-${{ matrix.cuda-version }}.0::libnpp nvidia::cuda-nvrtc=${{ matrix.cuda-version }} nvidia::cuda-toolkit=${{ matrix.cuda-version }} nvidia::cuda-cudart=${{ matrix.cuda-version }} nvidia::cuda-driver-dev=${{ matrix.cuda-version }} + - name: Install test dependencies + run: | + ${CONDA_RUN} python -m pip install --pre torchvision --index-url https://download.pytorch.org/whl/nightly/cpu + # Ideally we would find a way to get those dependencies from pyproject.toml + ${CONDA_RUN} python -m pip install numpy pytest pillow + + - name: Delete the src/ folder just for fun + run: | + # The only reason we checked-out the repo is to get access to the + # tests. We don't care about the rest. Out of precaution, we delete + # the src/ folder to be extra sure that we're running the code from + # the installed wheel rather than from the source. + # This is just to be extra cautious and very overkill because a) + # there's no way the `torchcodec` package from src/ can be found from + # the PythonPath: the main point of `src/` is precisely to protect + # against that and b) if we ever were to execute code from + # `src/torchcodec`, it would fail loudly because the built .so files + # aren't present there. + rm -r src/ + ls + - name: Smoke test + run: | + ${CONDA_RUN} python test/decoders/manual_smoke_test.py + - name: Run Python tests + run: | + # We skip test_get_ffmpeg_version because it may not have a micro version. + ${CONDA_RUN} FAIL_WITHOUT_CUDA=1 pytest test -k "not test_get_ffmpeg_version" -vvv + - name: Run Python benchmark + run: | + ${CONDA_RUN} time python benchmarks/decoders/gpu_benchmark.py --devices=cuda:0,cpu --resize_devices=none diff --git a/packaging/post_build_script.sh b/packaging/post_build_script.sh index 84686d84..721f0b61 100755 --- a/packaging/post_build_script.sh +++ b/packaging/post_build_script.sh @@ -6,7 +6,14 @@ wheel_path=$(pwd)/$(find dist -type f -name "*.whl") echo "Wheel content:" unzip -l $wheel_path -for ffmpeg_major_version in 4 5 6 7; do +ffmpeg_versions=(4 5 6 7) + +# TODO: Make ffmpeg4 work with nvcc. +if [ "$ENABLE_CUDA" -eq 1 ]; then + ffmpeg_versions=(5 6 7) +fi + +for ffmpeg_major_version in ${ffmepg_versions[@]}; do assert_in_wheel $wheel_path torchcodec/libtorchcodec${ffmpeg_major_version}.so done assert_not_in_wheel $wheel_path libtorchcodec.so diff --git a/src/torchcodec/decoders/_core/CMakeLists.txt b/src/torchcodec/decoders/_core/CMakeLists.txt index 2527c217..22bcd654 100644 --- a/src/torchcodec/decoders/_core/CMakeLists.txt +++ b/src/torchcodec/decoders/_core/CMakeLists.txt @@ -37,7 +37,7 @@ function(make_torchcodec_library library_name ffmpeg_target) set(NEEDED_LIBRARIES ${ffmpeg_target} ${TORCH_LIBRARIES} ${Python3_LIBRARIES}) if(ENABLE_CUDA) - list(APPEND NEEDED_LIBRARIES ${CUDA_CUDA_LIBRARY} + list(APPEND NEEDED_LIBRARIES ${CUDA_nppi_LIBRARY} ${CUDA_nppicc_LIBRARY} ) endif() target_link_libraries( @@ -76,10 +76,15 @@ if(DEFINED ENV{BUILD_AGAINST_ALL_FFMPEG_FROM_S3}) ${CMAKE_CURRENT_SOURCE_DIR}/fetch_and_expose_non_gpl_ffmpeg_libs.cmake ) - make_torchcodec_library(libtorchcodec4 ffmpeg4) - make_torchcodec_library(libtorchcodec5 ffmpeg5) - make_torchcodec_library(libtorchcodec6 ffmpeg6) - make_torchcodec_library(libtorchcodec7 ffmpeg7) + + if(NOT ENABLE_CUDA) + # TODO: Enable more ffmpeg versions for cuda. + make_torchcodec_library(libtorchcodec4 ffmpeg4) + endif() + make_torchcodec_library(libtorchcodec7 ffmpeg7) + make_torchcodec_library(libtorchcodec6 ffmpeg6) + make_torchcodec_library(libtorchcodec5 ffmpeg5) + else() message( STATUS diff --git a/test/utils.py b/test/utils.py index 99f53d33..a32ede2b 100644 --- a/test/utils.py +++ b/test/utils.py @@ -15,6 +15,8 @@ # Decorator for skipping CUDA tests when CUDA isn't available def needs_cuda(test_item): if not torch.cuda.is_available(): + if os.environ.get("FAIL_WITHOUT_CUDA") == "1": + raise RuntimeError("CUDA is required for this test") return pytest.mark.skip(reason="CUDA not available")(test_item) return test_item