Repository navigation
Add Metal backend support on Apple platforms #3717
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 73 commits
4b43e77
511719d
ce62cb1
80a39a8
59c82ec
beb3562
ef63903
e61559c
a5d166d
8eec508
b088a47
e88c5e2
deaf39b
924467d
4cffcae
885092e
5b193c4
c777d2d
00880fd
2239e2c
d23cc88
cb4749d
fed0c27
0f0abcc
3397269
3dd83c1
4473734
f6a1a9c
891acfb
1db81ac
fd36e5c
45a1a61
56be752
54d2f0d
73be597
64f470a
7e013eb
653e2cb
1e65b15
0f94f5a
d9a9133
bc1add3
fdf3e1f
8e36ac3
4c5d5ba
42d7390
45bf4a5
f769936
d3104fe
37c3fcf
815ef70
9576dd0
ac97041
9ebdbea
9dcc3be
e1923d1
48ffdcc
0f40e78
f02c5dc
7ddff1a
abb1d39
b6ec8e8
eaddca9
f80d2c0
52a97fa
e6ef33f
44eec4a
7712322
04de461
47b1739
1140b42
d079847
8871f97
3029423
71c176a
0cc9eeb
3d4133f
1ddf02f
3fa7d40
73ef4e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,15 @@ | ||
| cmake_minimum_required(VERSION 3.13) | ||
|
|
||
| # The iOS toolchain (third_party/vendor_tools/ios.toolchain.cmake) reads DEPLOYMENT_TARGET while | ||
| # configuring the project and derives both the deployment target and the compiler target triple | ||
| # from it. Apple toolchains reject iOS deployment targets below 15.0 (Xcode 26/27 SDKs), and | ||
| # the tgfx Metal backend has its own >= 13.0 floor that 15.0 satisfies, so Metal builds must | ||
| # pre-set the minimum before configuration. This only applies to iOS builds; macOS keeps its own | ||
| # default. Callers can override with -DDEPLOYMENT_TARGET=... explicitly. | ||
| if (PAG_USE_METAL AND NOT DEFINED DEPLOYMENT_TARGET AND PLATFORM MATCHES "^(OS|SIMULATOR)") | ||
| set(DEPLOYMENT_TARGET "15.0" CACHE STRING "Minimum iOS deployment target" FORCE) | ||
| endif () | ||
|
|
||
| project(PAG) | ||
|
|
||
| if (APPLE) | ||
|
|
@@ -25,6 +36,10 @@ set(CMAKE_CXX_VISIBILITY_PRESET hidden) | |
|
|
||
| # Options for building libpag | ||
| option(PAG_USE_OPENGL "Allow use of OpenGL as GPU backend" ON) | ||
| option(PAG_USE_METAL "Use Metal as the GPU backend on Apple platforms" OFF) | ||
| option(PAG_USE_VULKAN "Use Vulkan as the GPU backend" OFF) | ||
| option(PAG_USE_D3D12 "Use D3D12 as the GPU backend on Windows" OFF) | ||
| option(PAG_USE_WEBGPU "Use WebGPU as the GPU backend on Web" OFF) | ||
| option(PAG_USE_SWIFTSHADER "Allow build with the SwiftShader library" OFF) | ||
| option(PAG_USE_ANGLE "Allow build with the ANGLE library" OFF) | ||
| option(PAG_USE_QT "Allow build with QT frameworks" OFF) | ||
|
|
@@ -87,6 +102,62 @@ if (MACOS AND CLION_IDE AND NOT HasParent) | |
| option(PAG_BUILD_TESTS "Build libpag tests" ON) | ||
| endif () | ||
|
|
||
| # Non-GL backends (Metal / Vulkan / D3D12 / WebGPU) are mutually exclusive with each other and | ||
| # with OpenGL. If any non-GL backend is enabled, disable OpenGL and all GL-only variants | ||
| # (QT / SwiftShader / ANGLE). This mirrors the mutual-exclusion contract enforced in | ||
| # third_party/tgfx/CMakeLists.txt, and must run BEFORE the existing QT / SwiftShader mutual- | ||
| # exclusion block so the "if (PAG_USE_QT OR PAG_USE_SWIFTSHADER OR PAG_USE_ANGLE) set(PAG_USE_OPENGL ON)" | ||
| # fixup further down does not accidentally re-enable GL. | ||
| # | ||
| # The non-GL backends must not be combined: libpag passes each PAG_USE_* to tgfx unchanged, but | ||
| # tgfx resolves them with its own priority chain (Vulkan > D3D12 > Metal > WebGPU) and silently | ||
| # disables the losers. A combination would leave libpag compiling backend glue (Devices_*.cpp) | ||
| # for a backend tgfx never built, failing at link time with undefined symbols. | ||
| set(PAG_NON_GL_BACKENDS_COUNT 0) | ||
| foreach (backend PAG_USE_METAL PAG_USE_VULKAN PAG_USE_D3D12 PAG_USE_WEBGPU) | ||
| if (${backend}) | ||
| math(EXPR PAG_NON_GL_BACKENDS_COUNT "${PAG_NON_GL_BACKENDS_COUNT} + 1") | ||
| endif () | ||
| endforeach () | ||
| if (PAG_NON_GL_BACKENDS_COUNT GREATER 1) | ||
| message(FATAL_ERROR | ||
| "Only one non-OpenGL backend may be enabled at a time " | ||
| "(PAG_USE_METAL / PAG_USE_VULKAN / PAG_USE_D3D12 / PAG_USE_WEBGPU).") | ||
| endif () | ||
|
|
||
| if (PAG_USE_METAL OR PAG_USE_VULKAN OR PAG_USE_D3D12 OR PAG_USE_WEBGPU) | ||
| foreach (gl_variant PAG_USE_OPENGL PAG_USE_QT PAG_USE_SWIFTSHADER PAG_USE_ANGLE) | ||
| if (${gl_variant}) | ||
| message(WARNING "${gl_variant} is ignored because a non-OpenGL backend is enabled.") | ||
| endif () | ||
| endforeach () | ||
| set(PAG_USE_OPENGL OFF) | ||
| set(PAG_USE_QT OFF) | ||
| set(PAG_USE_SWIFTSHADER OFF) | ||
| set(PAG_USE_ANGLE OFF) | ||
| endif () | ||
|
|
||
| # The Metal backend requires iOS 13+ (see the tgfx deployment-target check) and supports the iOS | ||
| # simulator on arm64 only. Raise the deployment target automatically when Metal is selected so | ||
| # `gen_ios -DPAG_USE_METAL=ON` needs no extra flags (as documented in ios/README.md). Without this | ||
| # the toolchain's iOS default (9.0) flows into tgfx and trips its >= 13.0 check. Must run before | ||
| # tgfx is added via add_subdirectory(). Metal also only exists on Apple platforms — reject it | ||
| # elsewhere up front instead of failing later with a pile of undefined symbols. | ||
| if (PAG_USE_METAL AND NOT APPLE) | ||
| message(FATAL_ERROR "PAG_USE_METAL is only supported on Apple platforms.") | ||
| endif () | ||
| # The Metal backend supports the iOS simulator on arm64 only. | ||
| if (PAG_USE_METAL AND IOS) | ||
| set(CMAKE_XCODE_ATTRIBUTE_EXCLUDED_ARCHS[sdk=iphonesimulator*] "x86_64") | ||
| endif () | ||
|
|
||
| if (NOT PAG_USE_OPENGL AND NOT PAG_USE_METAL AND NOT PAG_USE_VULKAN | ||
| AND NOT PAG_USE_D3D12 AND NOT PAG_USE_WEBGPU) | ||
| message(FATAL_ERROR | ||
| "At least one GPU backend must be enabled " | ||
| "(PAG_USE_OPENGL / PAG_USE_METAL / PAG_USE_VULKAN / PAG_USE_D3D12 / PAG_USE_WEBGPU).") | ||
| endif () | ||
|
|
||
| if (PAG_USE_QT) | ||
| set(PAG_USE_SWIFTSHADER OFF) | ||
| set(PAG_USE_ANGLE OFF) | ||
|
|
@@ -120,6 +191,26 @@ if (PAG_USE_QT OR PAG_USE_SWIFTSHADER OR PAG_USE_ANGLE) | |
| set(PAG_USE_OPENGL ON) | ||
| endif () | ||
|
|
||
| # Expose the selected tgfx backend to libpag sources. The Devices_* implementation files dispatch | ||
| # on this macro to pick the right tgfx backend headers; kept in sync with the TGFX_USE_* variables | ||
| # passed to tgfx further down. libpag defines its own copy instead of relying on tgfx's PRIVATE | ||
| # compile definitions, so tgfx can change its visibility policy without breaking libpag. | ||
| if (PAG_USE_OPENGL) | ||
| list(APPEND PAG_DEFINES TGFX_USE_OPENGL) | ||
| endif () | ||
| if (PAG_USE_METAL) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Major] 非 GL 后端之间没有互斥,且 libpag 与 tgfx 的选择优先级相反 → 链接期 undefined symbol 证据链:
于是 macOS 上 放大器是 建议:
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 已在 commit 54d2f0d 中修复:按建议补了非 GL 后端之间的互斥——CMake configure 阶段统计 |
||
| list(APPEND PAG_DEFINES TGFX_USE_METAL) | ||
| endif () | ||
| if (PAG_USE_VULKAN) | ||
| list(APPEND PAG_DEFINES TGFX_USE_VULKAN) | ||
| endif () | ||
| if (PAG_USE_D3D12) | ||
| list(APPEND PAG_DEFINES TGFX_USE_D3D12) | ||
| endif () | ||
| if (PAG_USE_WEBGPU) | ||
| list(APPEND PAG_DEFINES TGFX_USE_WEBGPU) | ||
| endif () | ||
|
|
||
| if (PAG_USE_FFAVC) | ||
| set(PAG_USE_LIBAVC OFF) | ||
| endif () | ||
|
|
@@ -201,6 +292,13 @@ file(GLOB_RECURSE SRC_FILES | |
| src/base/*.* | ||
| src/codec/*.* | ||
| src/rendering/*.*) | ||
| # Devices_Metal.mm is an Objective-C++ file; it only has content under TGFX_USE_METAL. On non-Apple | ||
| # platforms the GCC toolchain lacks the Objective-C++ frontend (`cc1objplus`) and would fail even | ||
| # compiling an empty .mm. Drop it from the source list outside of Apple. | ||
| if (NOT APPLE) | ||
| list(FILTER SRC_FILES EXCLUDE REGEX "src/rendering/gpu/Devices_Metal\\.mm$") | ||
| list(FILTER SRC_FILES EXCLUDE REGEX "src/base/utils/TGFXCast_Metal\\.mm$") | ||
| endif () | ||
| list(APPEND PAG_FILES ${SRC_FILES}) | ||
|
|
||
| if (PAG_BUILD_PAGX) | ||
|
|
@@ -465,8 +563,7 @@ elseif (IOS) | |
| find_library(OpenGLES_LIBS OpenGLES REQUIRED) | ||
| list(APPEND PAG_SHARED_LIBS ${OpenGLES_LIBS}) | ||
| list(APPEND PAG_DEFINES GLES_SILENCE_DEPRECATION) | ||
| else () | ||
| # Uses Metal instead. | ||
| elseif (PAG_USE_METAL) | ||
| find_library(Metal_LIBS Metal REQUIRED) | ||
| list(APPEND PAG_SHARED_LIBS ${Metal_LIBS}) | ||
| endif () | ||
|
|
@@ -508,8 +605,7 @@ elseif (MACOS) | |
| find_library(OpenGL_LIBS OpenGL REQUIRED) | ||
| list(APPEND PAG_SHARED_LIBS ${OpenGL_LIBS}) | ||
| list(APPEND PAG_DEFINES GL_SILENCE_DEPRECATION) | ||
| else () | ||
| # Uses Metal instead. | ||
| elseif (PAG_USE_METAL) | ||
| find_library(Metal_LIBS Metal REQUIRED) | ||
| list(APPEND PAG_SHARED_LIBS ${Metal_LIBS}) | ||
| endif () | ||
|
|
@@ -665,6 +761,10 @@ elseif (HAS_CUSTOM_TGFX_DIR) | |
| message("TGFX_DIR: ${TGFX_DIR}") | ||
| list(APPEND PAG_INCLUDES ${TGFX_DIR}/include) | ||
| set(TGFX_USE_OPENGL ${PAG_USE_OPENGL}) | ||
| set(TGFX_USE_METAL ${PAG_USE_METAL}) | ||
| set(TGFX_USE_VULKAN ${PAG_USE_VULKAN}) | ||
| set(TGFX_USE_D3D12 ${PAG_USE_D3D12}) | ||
| set(TGFX_USE_WEBGPU ${PAG_USE_WEBGPU}) | ||
| set(TGFX_USE_QT ${PAG_USE_QT}) | ||
| set(TGFX_USE_SWIFTSHADER ${PAG_USE_SWIFTSHADER}) | ||
| set(TGFX_USE_ANGLE ${PAG_USE_ANGLE}) | ||
|
|
@@ -689,6 +789,10 @@ else () | |
| if (EXISTS ${TGFX_CACHE_DIR}) | ||
| message("TGFX_CACHE_DIR: ${TGFX_CACHE_DIR}") | ||
| list(APPEND TGFX_OPTIONS "-DTGFX_USE_OPENGL=${PAG_USE_OPENGL}") | ||
| list(APPEND TGFX_OPTIONS "-DTGFX_USE_METAL=${PAG_USE_METAL}") | ||
| list(APPEND TGFX_OPTIONS "-DTGFX_USE_VULKAN=${PAG_USE_VULKAN}") | ||
| list(APPEND TGFX_OPTIONS "-DTGFX_USE_D3D12=${PAG_USE_D3D12}") | ||
| list(APPEND TGFX_OPTIONS "-DTGFX_USE_WEBGPU=${PAG_USE_WEBGPU}") | ||
| list(APPEND TGFX_OPTIONS "-DTGFX_USE_QT=${PAG_USE_QT}") | ||
| list(APPEND TGFX_OPTIONS "-DTGFX_USE_SWIFTSHADER=${PAG_USE_SWIFTSHADER}") | ||
| list(APPEND TGFX_OPTIONS "-DTGFX_USE_ANGLE=${PAG_USE_ANGLE}") | ||
|
|
@@ -723,6 +827,10 @@ else () | |
| else () | ||
| message("TGFX_DIR: ${TGFX_DIR}") | ||
| set(TGFX_USE_OPENGL ${PAG_USE_OPENGL}) | ||
| set(TGFX_USE_METAL ${PAG_USE_METAL}) | ||
| set(TGFX_USE_VULKAN ${PAG_USE_VULKAN}) | ||
| set(TGFX_USE_D3D12 ${PAG_USE_D3D12}) | ||
| set(TGFX_USE_WEBGPU ${PAG_USE_WEBGPU}) | ||
| set(TGFX_USE_QT ${PAG_USE_QT}) | ||
| set(TGFX_USE_SWIFTSHADER ${PAG_USE_SWIFTSHADER}) | ||
| set(TGFX_USE_ANGLE ${PAG_USE_ANGLE}) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -125,6 +125,22 @@ struct MtlTextureInfo { | ||||||||||
| void* texture = nullptr; | |||||||||||
| }; | |||||||||||
|
|
|||||||||||
| /** | |||||||||||
| * Types for interacting with Metal sync objects created externally to PAG. Holds an id<MTLEvent> | |||||||||||
| * and its timeline signal value as opaque handles. | |||||||||||
| */ | |||||||||||
| struct MtlEventInfo { | |||||||||||
| /** | |||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Major] 根因:tgfx 里 proxy 与 resource 并存两套 format,回读路径用的是「用户传入」那份。
而 失效点 1 — 回读红蓝互换(置信度高): 失效点 2 — 高级混合上屏(置信度中高,请确认): 触发条件:传 BGRA8Unorm 纹理且未显式设 另:本处文档说 "must set this field so tgfx can bind the texture correctly",两个论断都与实现相反——不设也能正确绑定,采样绑定路径也不看它( 建议:把
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 采纳了"从真实 MTLTexture 覆盖"的方向并更进一步——commit fd36e5c 直接删除了 |
|||||||||||
| * Pointer to id<MTLEvent>. | |||||||||||
| */ | |||||||||||
| void* event = nullptr; | |||||||||||
|
|
|||||||||||
| /** | |||||||||||
| * The timeline signal value paired with the event. | |||||||||||
| */ | |||||||||||
| uint64_t value = 0; | |||||||||||
| }; | |||||||||||
|
|
|||||||||||
| /** | |||||||||||
| * Types for interacting with Vulkan resources created externally to PAG. Holds the VkImage as a | |||||||||||
| * void*. | |||||||||||
|
|
@@ -312,19 +328,44 @@ class PAG_API BackendSemaphore { | ||||||||||
| public: | |||||||||||
| BackendSemaphore(); | |||||||||||
|
|
|||||||||||
| bool isInitialized() const { | |||||||||||
| return _isInitialized; | |||||||||||
| /** | |||||||||||
| * Returns true if the backend semaphore has been initialized. | |||||||||||
| */ | |||||||||||
| bool isInitialized() const; | |||||||||||
|
|
|||||||||||
| /** | |||||||||||
| * Returns the backend API of this semaphore. | |||||||||||
| */ | |||||||||||
| Backend backend() const { | |||||||||||
| return _backend; | |||||||||||
| } | |||||||||||
|
|
|||||||||||
| void initGL(void* sync); | |||||||||||
|
|
|||||||||||
| void* glSync() const; | |||||||||||
|
|
|||||||||||
| /** | |||||||||||
| * Initializes the semaphore with a Metal id<MTLEvent> and a signal value. The event pointer is | |||||||||||
| * treated as an opaque handle — libpag never releases it. Metal's cross-queue synchronization | |||||||||||
| * always uses timeline (signaled/waited by counter value) semantics on MTLEvent, so both the | |||||||||||
| * event and its value are required. Passing a null event leaves the semaphore uninitialized. | |||||||||||
| */ | |||||||||||
| void initMetal(const MtlEventInfo& info); | |||||||||||
|
|
|||||||||||
| /** | |||||||||||
| * If the backend API is Metal, copies a snapshot of the MtlEventInfo struct into the passed in | |||||||||||
| * pointer and returns true. Otherwise, returns false if the backend API is not Metal. | |||||||||||
| */ | |||||||||||
| bool getMtlEventInfo(MtlEventInfo* info) const; | |||||||||||
|
|
|||||||||||
| private: | |||||||||||
| // The initialized state is derived from _backend plus the stored handle instead of a dedicated | |||||||||||
| // flag, which keeps sizeof(BackendSemaphore) and member offsets identical to earlier releases | |||||||||||
| // (binary compatibility). | |||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Minor] 「binary compatibility」的承诺不成立: main 上
修改建议:
实际受影响的场景较窄:旧头文件编译的 App 不重新编译、直接替换新的动态库。所以定为 Minor,但注释里的承诺应当修正。
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 同意,24 字节内确实无法兼容(偏移 16 无法同时容纳旧 bool 和 |
|||||||||||
| Backend _backend = Backend::MOCK; | |||||||||||
| union { | |||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Major] union 从
建议(可让这侧 ABI 破坏完全消失):删掉 改完收益是复合的:
那个多余的 bool 正是偏移漂移与 nullptr 不复位两个问题的共同根因。
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 完全采纳建议,已在 commit 0f0abcc 中按推荐方案修复:删除 |
|||||||||||
| void* _glSync; | |||||||||||
| MtlEventInfo _mtlInfo; | |||||||||||
| }; | |||||||||||
| bool _isInitialized; | |||||||||||
| }; | |||||||||||
| } // namespace pag | |||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[A9] 这段注释描述的是「Metal 选中时自动提升部署目标」,但下方 if 块只有 NOT APPLE 的 FATAL_ERROR 和 EXCLUDED_ARCHS,真正的部署目标设置在文件顶部 9-11 行;且注释写 iOS 13+,代码与 README 实际是 15.0。建议删除或合并到 9-11 行处并修正版本号。