Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 30 additions & 15 deletions src/chrono_vehicle/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,19 @@ set(CH_USE_SCM_GPU OFF)

# The GPU requirement is declared for this FEATURE, not for Chrono::Vehicle:
# the module builds fine with no GPU backend at all, and only the optional SCM
# GPU path needs one. The kernels are written in HIP and have no CUDA variant,
# hence REQUIRES HIP. Both HIP platforms are accepted: nothing here depends on
# the ROCm-only libraries (hipCUB/rocPRIM, rocThrust) that restrict
# Chrono::DEM and Chrono::FSI::SPH to the amd platform. So "HIP + ROCm on AMD"
# and "HIP + nvcc on NVIDIA" both give the GPU path with no further
# configuration.
# GPU path needs one. The kernels are backend-neutral (single-source .cu compiled as
# CUDA or HIP) and the host bridges are duplicated per backend under terrain/gpu/cuda
# and terrain/gpu/hip, so either toolchain is sufficient. Both HIP platforms are
# accepted: nothing here depends on the ROCm-only libraries (hipCUB/rocPRIM,
# rocThrust) that restrict Chrono::DEM and Chrono::FSI::SPH to the amd
# platform. So "HIP + ROCm on AMD" and "HIP + nvcc on NVIDIA" both give the GPU
# path with no further configuration.
if(CH_ENABLE_VEHICLE_SCM_GPU)

chrono_select_gpu_backend(CHRONO_VEHICLE_SCM_BACKEND
FEATURE "Chrono::Vehicle SCM GPU"
NAME VEHICLE_SCM
REQUIRES HIP
REQUIRES CUDA_OR_HIP
HIP_PLATFORMS amd nvidia)

if(CHRONO_VEHICLE_SCM_BACKEND STREQUAL "NONE")
Expand All @@ -65,7 +66,7 @@ if(CH_ENABLE_VEHICLE_SCM_GPU)
# silently build the CPU path. Leaving the cache alone lets availability be
# re-evaluated every configure.
chrono_gpu_feature_unavailable(FEATURE "Chrono::Vehicle SCM GPU"
REQUIRES "HIP"
REQUIRES "CUDA or HIP"
CLASS IMPLIED)

else()
Expand Down Expand Up @@ -318,22 +319,36 @@ endif()
source_group("terrain" FILES ${CV_TERRAIN_FILES})

if(CH_USE_SCM_GPU)
set(CV_SCM_GPU_HIP_FILES
terrain/gpu/SCMGpuKernels.hip.cpp
terrain/gpu/SCMRaycastGpuKernels.hip.cpp
# Single source for both backends: .cu is CUDA by default and
# chrono_set_gpu_source_language() relabels it LANGUAGE HIP when the HIP backend is
# selected, exactly as Chrono::DEM and Chrono::FSI::SPH do. The host bridges below are
# NOT shared -- they are almost entirely runtime API calls and are duplicated per backend.
set(CV_SCM_GPU_KERNEL_FILES
terrain/gpu/SCMGpuKernels.cu
terrain/gpu/SCMRaycastGpuKernels.cu
)
chrono_set_gpu_source_language(${CHRONO_VEHICLE_SCM_BACKEND} ${CV_SCM_GPU_HIP_FILES})
chrono_set_gpu_source_language(${CHRONO_VEHICLE_SCM_BACKEND} ${CV_SCM_GPU_KERNEL_FILES})
# Host bridges are duplicated, not shared: they are almost entirely runtime API calls
# (about thirty symbols), and Chrono has no compatibility layer for those. Exactly one
# directory is compiled per build; the two files in each pair must be kept in step by
# hand, which the banner at the top of each states.
if(CHRONO_VEHICLE_SCM_BACKEND STREQUAL "CUDA")
set(CV_SCM_GPU_HOST_DIR terrain/gpu/cuda)
else()
set(CV_SCM_GPU_HOST_DIR terrain/gpu/hip)
endif()

list(APPEND CV_TERRAIN_FILES
terrain/SCMGpu.h
terrain/SCMGpuTypes.h
terrain/SCMTerrainGpu.h
terrain/SCMTerrainGpu.cpp
terrain/gpu/SCMGpuHost.cpp
${CV_SCM_GPU_HOST_DIR}/SCMGpuHost.cpp
terrain/SCMRaycastGpu.h
terrain/SCMRaycastGpuTypes.h
terrain/SCMTerrainRaycastGpu.cpp
terrain/gpu/SCMRaycastGpuHost.cpp
${CV_SCM_GPU_HIP_FILES}
${CV_SCM_GPU_HOST_DIR}/SCMRaycastGpuHost.cpp
${CV_SCM_GPU_KERNEL_FILES}
)
endif()

Expand Down
2 changes: 0 additions & 2 deletions src/chrono_vehicle/terrain/SCMTerrainRaycastGpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
#include <unordered_map>
#include <vector>

#include <hip/hip_runtime.h>

#include "chrono/physics/ChBody.h"
#include "chrono/collision/ChCollisionShapeTriangleMesh.h"
#include "chrono/geometry/ChTriangleMeshConnected.h"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
// SCMGpuKernels.hip.cpp — HIP kernels for SCM contact forces.

#include <hip/hip_runtime.h>
// SCMGpuKernels.cu — SCM contact-force kernels, compiled as CUDA or HIP.

// Backend-neutral source: this file is compiled by nvcc for the CUDA backend and by hipcc for the HIP
// one, selected by chrono_set_gpu_source_language() -- the same single-source arrangement Chrono::DEM
// and Chrono::FSI::SPH use.
//
// The conditional below is not optional. nvcc implicitly includes <cuda_runtime.h> for a .cu, but
// HIP-clang provides nothing: a .cu with no includes compiles under nvcc and fails under
// `hipcc --offload-arch=gfx942` with threadIdx/blockIdx/blockDim undeclared.
//
// gpuStream_t and gpuGetLastError are the ONLY two runtime names this file touches; everything else
// here is device syntax, which CUDA and HIP spell identically. The host bridges, which use around
// thirty runtime symbols, are duplicated per backend instead -- they can be, and Chrono has no
// precedent for a compatibility layer. This file cannot be duplicated: it is the shared source.
// Which branch each build takes, verified rather than assumed:
// CUDA backend nvcc -> CUDA branch (nvcc implicitly includes
// <cuda_runtime.h> for a .cu)
// HIP on NVIDIA nvcc -D__HIP_PLATFORM_NVIDIA__ -> CUDA branch. CMake's HIP language calls
// nvcc directly here and defines neither
// __HIPCC__ nor __HIP_PLATFORM_AMD__; that
// is correct, because on this platform
// hipStream_t IS cudaStream_t.
// HIP on AMD hipcc / HIP-clang -> HIP branch, the only one that needs the
// header.
#if defined(__HIPCC__) || defined(__HIP_PLATFORM_AMD__)
#include <hip/hip_runtime.h>
using gpuStream_t = hipStream_t;
#define gpuGetLastError hipGetLastError
#else
using gpuStream_t = cudaStream_t;
#define gpuGetLastError cudaGetLastError
#endif

#include <cmath>
#include <cstring>
Expand Down Expand Up @@ -181,7 +210,7 @@ extern "C" int scm_launch_compute_forces(const void* soil_host,
const void* in_dev,
void* out_dev,
int n,
hipStream_t stream) {
gpuStream_t stream) {
if (n <= 0)
return 0;

Expand All @@ -194,15 +223,15 @@ extern "C" int scm_launch_compute_forces(const void* soil_host,
static_cast<const HitInputDev*>(in_dev),
static_cast<HitOutputDev*>(out_dev),
n);
return hipGetLastError();
return gpuGetLastError();
}

extern "C" int scm_launch_reduce_body_forces(const void* in_dev,
const void* out_dev,
void* body_forces_dev,
int n,
int n_bodies,
hipStream_t stream) {
gpuStream_t stream) {
if (n <= 0 || n_bodies <= 0)
return 0;

Expand All @@ -213,5 +242,5 @@ extern "C" int scm_launch_reduce_body_forces(const void* in_dev,
static_cast<double*>(body_forces_dev),
n,
n_bodies);
return hipGetLastError();
return gpuGetLastError();
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SCMRaycastGpuKernels.hip.cppHIP kernel for the SCM GPU ray-cast backend.
// SCMRaycastGpuKernels.curay-cast kernel for the SCM GPU backend, compiled as CUDA or HIP.
//
// One thread BLOCK per query (SCM grid node ray), not one thread. Threads within a block split the
// triangle scan (each thread handles a strided subset), transform each triangle from local to world
Expand All @@ -13,18 +13,50 @@
// ~2400-3400 x kThreadsPerRay threads, without changing the total amount of work
// (queries x triangles) or the algorithm itself.
//
// Templated on Real (double or float) so the same kernel logic runs at either precision. double is the
// validated default for this project's AMD MI300X target. float is offered for GPUs with weak double-precision throughput -- notably consumer NVIDIA
// cards (RTX 4080/5090-class), where FP64 is deliberately throttled relative to FP32 (unlike MI300X, a
// proper datacenter part) -- so a straight double-precision port would be correct there but far slower
// than it needs to be. Both precisions are exported (scm_launch_raycast_fp64 / _fp32); the host bridge
// (SCMRaycastGpuHost.cpp) selects one per-context based on ScmRaycastGpuPrecision, which
// SCMTerrainRaycastGpu.cpp defaults by which HIP platform (AMD vs NVIDIA) this build targets.
// Templated on Real (double or float) so the same kernel logic runs at either precision. FP32 is the
// default, on every backend and every target: making it depend on the hardware would let the same
// model diverge between machines, which is worse than the precision itself. See the comment on
// DesiredRaycastGpuPrecision in SCMTerrainRaycastGpu.cpp for the validation behind that choice.
// FP32 also matters on GPUs with weak double-precision throughput -- notably consumer NVIDIA cards
// (RTX 4080/5090-class), where FP64 is deliberately throttled relative to FP32, unlike a datacenter
// part such as the MI300X -- so a double-only port would be correct there but far slower than it
// needs to be. Both precisions are exported (scm_launch_raycast_fp64 / _fp32) and the host bridge
// (SCMRaycastGpuHost.cpp) selects one per context from ScmRaycastGpuPrecision; env
// SCM_RAYCAST_GPU_PRECISION=fp32|fp64 overrides the default at run time.
//
// Includes the empirically-determined margin-correction sign; an exact match to Bullet's hit set is
// not the goal.

#include <hip/hip_runtime.h>
// Backend-neutral source: this file is compiled by nvcc for the CUDA backend and by hipcc for the HIP
// one, selected by chrono_set_gpu_source_language() -- the same single-source arrangement Chrono::DEM
// and Chrono::FSI::SPH use.
//
// The conditional below is not optional. nvcc implicitly includes <cuda_runtime.h> for a .cu, but
// HIP-clang provides nothing: a .cu with no includes compiles under nvcc and fails under
// `hipcc --offload-arch=gfx942` with threadIdx/blockIdx/blockDim undeclared.
//
// gpuStream_t and gpuGetLastError are the ONLY two runtime names this file touches; everything else
// here is device syntax, which CUDA and HIP spell identically. The host bridges, which use around
// thirty runtime symbols, are duplicated per backend instead -- they can be, and Chrono has no
// precedent for a compatibility layer. This file cannot be duplicated: it is the shared source.
// Which branch each build takes, verified rather than assumed:
// CUDA backend nvcc -> CUDA branch (nvcc implicitly includes
// <cuda_runtime.h> for a .cu)
// HIP on NVIDIA nvcc -D__HIP_PLATFORM_NVIDIA__ -> CUDA branch. CMake's HIP language calls
// nvcc directly here and defines neither
// __HIPCC__ nor __HIP_PLATFORM_AMD__; that
// is correct, because on this platform
// hipStream_t IS cudaStream_t.
// HIP on AMD hipcc / HIP-clang -> HIP branch, the only one that needs the
// header.
#if defined(__HIPCC__) || defined(__HIP_PLATFORM_AMD__)
#include <hip/hip_runtime.h>
using gpuStream_t = hipStream_t;
#define gpuGetLastError hipGetLastError
#else
using gpuStream_t = cudaStream_t;
#define gpuGetLastError cudaGetLastError
#endif

#include <cstdint>

Expand Down Expand Up @@ -309,7 +341,7 @@ int launch(const void* queries_dev,
const void* margins_dev,
int n_bodies,
void* results_dev,
hipStream_t stream) {
gpuStream_t stream) {
if (n_queries <= 0)
return 0;

Expand All @@ -322,7 +354,7 @@ int launch(const void* queries_dev,
static_cast<const MarginDevT<Real>*>(margins_dev),
n_bodies,
static_cast<ResultDevT<Real>*>(results_dev));
return hipGetLastError();
return gpuGetLastError();
}

} // namespace
Expand All @@ -336,7 +368,7 @@ extern "C" int scm_launch_raycast_fp64(const void* queries_dev,
const void* margins_dev,
int n_bodies,
void* results_dev,
hipStream_t stream) {
gpuStream_t stream) {
return launch<double>(queries_dev, n_queries, verts_dev, faces_dev, n_faces, xforms_dev, margins_dev, n_bodies,
results_dev, stream);
}
Expand All @@ -350,7 +382,7 @@ extern "C" int scm_launch_raycast_fp32(const void* queries_dev,
const void* margins_dev,
int n_bodies,
void* results_dev,
hipStream_t stream) {
gpuStream_t stream) {
return launch<float>(queries_dev, n_queries, verts_dev, faces_dev, n_faces, xforms_dev, margins_dev, n_bodies,
results_dev, stream);
}
Loading