Skip to content

Commit 93e2a54

Browse files
authored
GPU: Apple Metal backend, off by default (#15859)
Initial work, requires XCode 27 and Metal 4.1.
1 parent 5d17f3f commit 93e2a54

9 files changed

Lines changed: 911 additions & 36 deletions
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
# All rights not expressly granted are reserved.
4+
#
5+
# This software is distributed under the terms of the GNU General Public
6+
# License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
#
8+
# In applying this license CERN does not waive the privileges and immunities
9+
# granted to it by virtue of its status as an Intergovernmental Organization
10+
# or submit itself to any jurisdiction.
11+
12+
set(MODULE GPUTrackingMETAL)
13+
enable_language(ASM)
14+
15+
message(STATUS "Building GPUTracking with Metal support")
16+
17+
# convenience variables
18+
if(ALIGPU_BUILD_TYPE STREQUAL "Standalone")
19+
set(GPUDIR ${CMAKE_SOURCE_DIR}/../)
20+
else()
21+
set(GPUDIR ${CMAKE_SOURCE_DIR}/GPU/GPUTracking)
22+
endif()
23+
set(METAL_SRC ${GPUDIR}/Base/metal/GPUReconstructionMETAL.metal)
24+
set(METAL_BIN ${CMAKE_CURRENT_BINARY_DIR}/GPUReconstructionMetalCode)
25+
26+
# MSL 4.1 is the first version with a generic address space: earlier versions
27+
# reject an unannotated pointer or `this` outright, which GPUCommonDefAPI.h
28+
# relies on for GPUgeneric() and GPUdDefault().
29+
set(METAL_FLAGS -std=metal4.1 ${GPUCA_METAL_DENORMALS_FLAGS})
30+
if(GPUCA_DETERMINISTIC_MODE GREATER_EQUAL ${GPUCA_DETERMINISTIC_MODE_MAP_NO_FAST_MATH})
31+
set(METAL_FLAGS ${METAL_FLAGS} ${GPUCA_METAL_NO_FAST_MATH_FLAGS})
32+
endif()
33+
set(METAL_DEFINES "-D$<JOIN:$<TARGET_PROPERTY:O2::GPUTracking,COMPILE_DEFINITIONS>,$<SEMICOLON>-D>"
34+
"-I$<JOIN:$<FILTER:$<TARGET_PROPERTY:O2::GPUTracking,INCLUDE_DIRECTORIES>,EXCLUDE,^/usr/include/?>,$<SEMICOLON>-I>"
35+
-I${CMAKE_SOURCE_DIR}/Detectors/TRD/base/src
36+
-I${CMAKE_SOURCE_DIR}/Detectors/Base/src
37+
-I${CMAKE_SOURCE_DIR}/DataFormats/Reconstruction/src
38+
)
39+
40+
set(SRCS GPUReconstructionMetal.mm GPUReconstructionMetalKernels.mm)
41+
set(HDRS GPUReconstructionMetal.h GPUReconstructionMetalIncludesHost.h)
42+
43+
if(ALIGPU_BUILD_TYPE STREQUAL "O2")
44+
o2_add_library(${MODULE}
45+
SOURCES ${SRCS}
46+
PUBLIC_LINK_LIBRARIES O2::GPUTracking
47+
TARGETVARNAME targetName)
48+
49+
target_link_libraries(${targetName} PUBLIC ${METAL_FRAMEWORKS})
50+
51+
target_compile_definitions(${targetName} PRIVATE $<TARGET_PROPERTY:O2::GPUTracking,COMPILE_DEFINITIONS>)
52+
# the compile_defitions are not propagated automatically on purpose (they are
53+
# declared PRIVATE) so we are not leaking them outside of the GPU**
54+
# directories
55+
endif()
56+
57+
if(ALIGPU_BUILD_TYPE STREQUAL "Standalone")
58+
add_library(${MODULE} SHARED ${SRCS})
59+
target_link_libraries(${MODULE} GPUTracking)
60+
install(TARGETS ${MODULE})
61+
set(targetName ${MODULE})
62+
endif()
63+
64+
if(METAL_ENABLED) # BUILD Metal source code for runtime compilation target
65+
66+
# executes clang to preprocess
67+
add_custom_command(
68+
OUTPUT ${METAL_BIN}.metal
69+
COMMAND xcrun -sdk macosx metal
70+
-Wno-unused-command-line-argument
71+
${METAL_FLAGS}
72+
${METAL_DEFINES}
73+
-MD -MT ${METAL_BIN}.src -MF ${METAL_BIN}.src.d
74+
-E -P ${METAL_SRC} > ${METAL_BIN}.metal
75+
DEPENDS ${METAL_SRC}
76+
DEPFILE ${METAL_BIN}.src.d
77+
COMMAND_EXPAND_LISTS
78+
COMMENT "Preparing Metal source file for run time compilation ${METAL_BIN}.metal")
79+
80+
# Create the ir
81+
add_custom_command(
82+
OUTPUT ${METAL_BIN}.ir
83+
COMMAND xcrun -sdk macosx metal
84+
-Wno-unused-command-line-argument
85+
-Wno-c++17-extensions
86+
-ferror-limit=10000
87+
${METAL_FLAGS}
88+
${METAL_DEFINES}
89+
${METAL_BIN}.metal
90+
-o ${METAL_BIN}.ir
91+
DEPENDS ${METAL_BIN}.metal
92+
COMMAND_EXPAND_LISTS
93+
COMMENT "Preparing Metal intermediate representation for run time compilation ${METAL_BIN}.ir")
94+
95+
add_custom_target(metal_preprocessed_code ALL DEPENDS ${METAL_BIN}.metal COMMENT "Needed to inject dependency on its creation")
96+
add_custom_target(metal_intermediate_representation ALL DEPENDS ${METAL_BIN}.ir COMMENT "Needed to inject dependency on its creation")
97+
98+
# Pack the compiled library into __DATA,__gpu_resource during final link. This
99+
# way we do not need to create an intermediate object. Compiling the source at
100+
# run time is not an option: the driver's compiler service dies on it.
101+
target_link_options(${targetName}
102+
PRIVATE
103+
"-Wl,-sectcreate,__DATA,__gpu_resource,${METAL_BIN}.ir")
104+
add_dependencies(${targetName} metal_preprocessed_code)
105+
add_dependencies(${targetName} metal_intermediate_representation)
106+
endif()
107+
108+
install(FILES ${HDRS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/GPU)
109+
110+
target_compile_definitions(${targetName} PRIVATE GPUCA_METAL_BUILD_FLAGS=$<JOIN:${METAL_FLAGS},\ > )
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright 2019-2025 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
/// \file GPUReconstructionMETAL.metal
13+
14+
#pragma clang diagnostic push
15+
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
16+
// clang-format off
17+
18+
// --- Backend selection -------------------------------------------------------
19+
#define GPUCA_GPUTYPE_METAL 1
20+
21+
// --- Metal stdlib ------------------------------------------------------------
22+
#include <metal_stdlib>
23+
// MSL rejects derived classes outside of this pragma, and the kernels are
24+
// class-based throughout. metal_stdlib itself uses it in 46 paired places.
25+
#pragma METAL internals : enable
26+
using namespace metal;
27+
28+
// --- OpenCL compatibility shims ---------------------------------------------
29+
30+
// Address space aliases (match OpenCL vernacular used by the project); constant
31+
// is spelled the same in MSL
32+
#define global device
33+
#define local threadgroup
34+
35+
#ifndef M_PI
36+
#define M_PI 3.1415926535f
37+
#endif
38+
39+
// Disable assertions inside GPU code (same as OpenCL variant)
40+
#ifdef assert
41+
# undef assert
42+
#endif
43+
#define assert(param)
44+
45+
// --- double ------------------------------------------------------------------
46+
// MSL has no double. GPUdoubleBinary64 is IEEE-754 binary64 in software, with the
47+
// same eight bytes in the same order, so the keyword can simply name it and the
48+
// shared code needs no separate spelling. Must come after metal_stdlib, which
49+
// uses the token itself.
50+
#include "GPUCommonDoubleBinary64.h"
51+
#define double o2::gpu::GPUdoubleBinary64
52+
#include "GPUCommonDouble.h"
53+
54+
// --- Project headers ---------------------------------------------------------
55+
#include "GPUCommonDef.h"
56+
#include "GPUCommonTypeTraits.h"
57+
#include "GPUCommonArray.h"
58+
59+
#include "GPUConstantMem.h"
60+
#include "GPUReconstructionIncludesDeviceAll.h"
61+
62+
// --- Kernel list expansion ---------------------------------------------------
63+
#define GPUCA_KRNL(...) GPUCA_KRNLGPU(__VA_ARGS__)
64+
65+
// --- Constant memory + global heap plumbing ---------------------------------
66+
// The heap and the constant memory arrive as buffer(0) and buffer(1). The latter
67+
// is untyped because a buffer of GPUConstantMem, which has base classes, is not
68+
// a valid kernel argument type.
69+
#define GPUCA_CONSMEM_PTR \
70+
device char* gpu_mem [[buffer(0)]], \
71+
device char* pConstantRaw [[buffer(1)]],
72+
#define GPUCA_CONSMEM (*(device GPUConstantMem*)pConstantRaw)
73+
74+
#include "GPUReconstructionKernelList.h"
75+
76+
// clang-format on
77+
#pragma clang diagnostic pop
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2019-2025 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
#ifndef GPURECONSTRUCTIONMETAL_H
13+
#define GPURECONSTRUCTIONMETAL_H
14+
15+
#include "GPUReconstructionDeviceBase.h"
16+
17+
extern "C" o2::gpu::GPUReconstruction* GPUReconstruction_Create_METAL(const o2::gpu::GPUSettingsDeviceBackend& cfg);
18+
19+
namespace o2::gpu
20+
{
21+
struct GPUReconstructionMetalInternals;
22+
23+
class GPUReconstructionMetal : public GPUReconstructionProcessing::KernelInterface<GPUReconstructionMetal, GPUReconstructionDeviceBase>
24+
{
25+
public:
26+
GPUReconstructionMetal(const GPUSettingsDeviceBackend& cfg);
27+
~GPUReconstructionMetal() override;
28+
29+
template <class T, int32_t I = 0, typename... Args>
30+
void runKernelBackend(const krnlSetupTime& _xyz, const Args&... args);
31+
32+
protected:
33+
int32_t InitDevice_Runtime() override;
34+
int32_t ExitDevice_Runtime() override;
35+
36+
virtual int32_t GPUChkErrInternal(const int64_t error, const char* file, int32_t line) const override;
37+
38+
void SynchronizeGPU() override;
39+
int32_t GPUDebug(const char* state = "UNKNOWN", int32_t stream = -1, bool force = false) override;
40+
void SynchronizeStream(int32_t stream) override;
41+
void SynchronizeEvents(deviceEvent* evList, int32_t nEvents = 1) override;
42+
void StreamWaitForEvents(int32_t stream, deviceEvent* evList, int32_t nEvents = 1) override;
43+
bool IsEventDone(deviceEvent* evList, int32_t nEvents = 1) override;
44+
45+
size_t WriteToConstantMemory(size_t offset, const void* src, size_t size, int32_t stream = -1, deviceEvent* ev = nullptr) override;
46+
size_t GPUMemCpy(void* dst, const void* src, size_t size, int32_t stream, int32_t toGPU, deviceEvent* ev = nullptr, deviceEvent* evList = nullptr, int32_t nEvents = 1) override;
47+
void ReleaseEvent(deviceEvent ev) override;
48+
void RecordMarker(deviceEvent* ev, int32_t stream) override;
49+
50+
template <class T, int32_t I = 0>
51+
int32_t AddKernel();
52+
53+
GPUReconstructionMetalInternals* mInternals;
54+
55+
template <class S, class T, int32_t I>
56+
S& getKernelObject();
57+
58+
int32_t GetMetalPrograms();
59+
60+
private:
61+
int32_t AddKernels();
62+
};
63+
64+
} // namespace o2::gpu
65+
66+
#endif // GPURECONSTRUCTIONMETAL_H

0 commit comments

Comments
 (0)