fix: macOS universal build#1171
Conversation
ASTC encoder library with matching SIMD instruction set will be added as dependency per architecture. When using the Xcode generator to build a universal binary, SSE 4.1 is enabled for x86_64 architecture only.
…her than Xcode as well.
…)`, which is known to generate broken linking paths in conjunction with object library dependencies.
…lds. chore: Removed outdated description about falling back to disabling SSE for Basis Universal on universal builds.
…_type`. Updated comments describing the outdated BasisU SSE behavior for multi-arch builds.
|
I noticed legacy code that sets compiler option It's effectively redundant now, and the per-arch block accidentally proves it. The target_compile_options at line 495 fires when The empirical evidence that it isn't actually needed even in that remaining case comes from the per-arch override
So the same logic applies to the single-arch x86_64 case: ktx doesn't need -msse4.1 either, only basisu_encoder does, Tradeoff: it's defensive code that's been there a long time, so removing it has a small chance of breaking some |
|
I noticed that currently it's not possible to force SSE off via To solve it I suggest to introduce option After that change the last message about the legacy compiler option may become obsolete. edit: Can confirm that the |
…ing support for SSE 4.1 in Basis Universal. The now internal `BASISU_SSE` used to be exposed prior.
|
@atteneder, I'll be away for the next 5 days. I will review when I return. I am puzzled by your statement that it is not possible to force SSE off via BASISU_SSE. I can see no deliberate reason for that. Perhaps there is an error in basis_universal's CMakeLists.txt. Make sure the CMake cache is cleared before running your tests. Maybe the value is coming from there. |
…ctice of using auto-dereferencing.
When cross-compiling to platforms other than iOS/tvOS/visionOS (e.g. Android) this was wrongfully set to `ON` leading to follow-up errors.
Much appreciated! No rush.
Apologies for the confusing statements. Of course if(APPLE_MAC_OS AND LIBKTX_FEATURE_BASISU_SSE)
####################################################
# Per-architecture SSE 4.1 in universal macOS builds.
####################################################
# basisu_encoder cannot enable SSE for the whole project in a universal build
# (the -msse4.1 flag would break the arm64 slice). With BASISU_SSE OFF, basisu
# emits BASISU_SUPPORT_SSE=0 PUBLIC and no -msse4.1; we then use clang's
# -Xarch_<arch> driver option to inject -msse4.1 and override
# BASISU_SUPPORT_SSE to 1 only for the x86_64 sub-invocation. -Xarch_x86_64
# applies to the immediately-following argument only, so each flag needs its
# own prefix. Works with any generator (Xcode, Ninja, Make) on Apple.
if(APPLE_MAC_OS_UNIVERSAL AND APPLE_MAC_OS_ARCH_x86_64)
# SHELL: prevents CMake from deduplicating the repeated -Xarch_x86_64
# tokens, which would otherwise leave only the first flag arch-qualified.
target_compile_options(basisu_encoder PRIVATE
"SHELL:-Xarch_x86_64 -msse4.1"
"SHELL:-Xarch_x86_64 -UBASISU_SUPPORT_SSE"
"SHELL:-Xarch_x86_64 -DBASISU_SUPPORT_SSE=1"
)
foreach(t ktx ktx_read)
if(TARGET ${t})
target_compile_options(${t} PRIVATE
"SHELL:-Xarch_x86_64 -UBASISU_SUPPORT_SSE"
"SHELL:-Xarch_x86_64 -DBASISU_SUPPORT_SSE=1"
)
endif()
endforeach()
endif()
endif()This ensures SSE is enabled for I needed to introduce I opened the PR as soon as the macOS universal builds were functional. There's still issues with some (cross-compiling) platform builds that I'm trying to address now. What to do with
|
|
Update: I've tested macOS Universal with Xcode and Ninja generators, but Unix Makefiles did not succeed. edit: I personally think that's not a blocker, but you be the judge. |
Supposed to fix compilation of ktx/ktxdiff tools against dynamic library (Windows).
MarkCallow
left a comment
There was a problem hiding this comment.
Looks okay but I have a lot of questions. Please answer them.
| # arch (e.g. "x86_64" or "arm64") or to a list ("arm64;x86_64") to | ||
| # request a universal build. The literal "$(ARCHS_STANDARD)" is | ||
| # rejected by the root CMakeLists.txt because CMake stores it | ||
| # unexpanded and breaks $<TARGET_OBJECTS:...> paths. |
There was a problem hiding this comment.
Please add a pointer to the detailed explanation of the problem in the root CMakeLists.txt.
Too bad we can't query the ARCHS_STANDARD build setting value and expand it ourselves.
There was a problem hiding this comment.
Will do.
And agreed, that would be nice.
That got me thinking and...what if we substitute the literal with hard-coded values and add a custom pre-build command to check if it (still) matches up:
if(ADD_XCODE_MACOS_ARCHS_STANDARD_CHECK)
add_custom_command(TARGET ${target} PRE_BUILD
COMMAND /bin/sh -c [[
expected="arm64 x86_64"
actual="$ARCHS_STANDARD"
if [ "$actual" != "$expected" ]; then
echo "ARCHS_STANDARD mismatch: got [$actual], expected [$expected]" >&2
exit 1
else
echo "ARCHS_STANDARD check passed: [$actual]"
fi
]]
VERBATIM
)
endif()I tested it and it worked (with both ONLY_ACTIVE_ARCH=NO and YES), so I'll add it.
There was a problem hiding this comment.
Will this work on Windows? If not, you can use cmake -P with cmake code to do the same check.
There was a problem hiding this comment.
I'd only add this on the conditions macOS target and Xcode generator, so it wouldn't affect other platforms.
How would that cmake -P solution look like? Maybe it's more elegant than this.
| # Building for iOS, iPadOS, etc. Since we don't care what | ||
| # type of ARM processor, arbitrarily set armv8. | ||
| # It should be arm64 but there is a check in tests/CMakeLists.txt | ||
| # that is dropping loadtests for Apple Silicon arm64. |
There was a problem hiding this comment.
Probably should fix this and tests/CMakeLists.txt. I don't recall why it wants to drop loadtests for Apple Silicon arm64.
| Macs are either based on Intel or the newer Apple Silicon architecture. By default CMake configures to build for your host's platform, whichever it is. If you want to cross compile universal binaries (that support both platforms), add the parameter `-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"` to cmake. | ||
|
|
||
| > **Known limitations:** | ||
| > - Intel Macs have support for SSE, but if you're building universal binaries, |
There was a problem hiding this comment.
This limitation is now outdated. Delete.
| set(APPLE_MAC_OS_UNIVERSAL OFF) | ||
| if(APPLE_MAC_OS) | ||
| # Check CMAKE_OSX_ARCHITECTURES for multiple architectures. | ||
| list_contains(CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD)" APPLE_MAC_OS_ARCH_STANDARD) |
There was a problem hiding this comment.
I thought we can't use $(ARCHS_STANDARD).
| # Set ordinary variable to override astc-encoder option's ON default | ||
| # and hide the option. | ||
| set(ASTCENC_UNIVERSAL_BUILD ${universal_build}) | ||
| set(ASTCENC_APPLE_MAC_OS_UNIVERSAL ON) |
There was a problem hiding this comment.
This is no longer an astc-encoder option, is it? So, at the least, the comment is inaccurate. But why create this new variable?
| if(APPLE_MAC_OS_UNIVERSAL) | ||
| if(APPLE_MAC_OS_ARCH_x86_64h) | ||
| list(APPEND ASTCENC_LIB_TARGETS astcenc-avx2-static) | ||
| elseif(APPLE_MAC_OS_ARCH_x86_64 OR APPLE_MAC_OS_ARCH_STANDARD) |
There was a problem hiding this comment.
By using APPLE_MAC_OS_ARCH_STANDARD aren't you enshrining the problem you mentioned somewhere else of failing to handle a new architecture added to $(ARCH_STANDARD)?
| # SHELL: prevents CMake from deduplicating the repeated -Xarch_x86_64 | ||
| # tokens, which would otherwise leave only the first flag arch-qualified. | ||
| target_compile_options(basisu_encoder PRIVATE | ||
| "SHELL:-Xarch_x86_64 -msse4.1" |
There was a problem hiding this comment.
How/where do these get set for a build that is just for x86_64 or x86_64h?
|
I think you are correct about the I originally introduced set_target_processor_type to determine whether the BASISU_SSE option should be shown or not, to remove a possible point of confusion for users. It wasn't connected to universal builds. I still want to avoid showing this option when it is of no use. I had hoped it would be temporary until basisu_encoder was changed to use compiler pre-defined macros and run-time queries to determine if SSE could be used and what variant. It doesn't look as if that is going to happen. They key here is to not show the BASISU_SSE option, now LIBKTX_FEATURE_BASISU_SSE, if the target processor does not support it. |
…utypetest.cmake` to `CMakeLists.txt`.
… target on macOS.
…ting.
Instead it's substituted by the currently known value ('arm64;x86_64') and a custom Xcode pre-build command certifies that that's still correct.
This reverts commit 7a67f3d. Android linking failed, because astc_codec.cpp depends on ASTC encoder targes.
Summary
Since ~4.4.0 I was not able to build a universal
libktxorlibktx_readmacOS binary anymore. Even though those will fade out with Apple deprecating Intel CPU support, I still want to support that for the time being.Here's what this PR does to fix that:
astc-encoder*targets may get added as dependency (in most casesastcenc-sse4.1-staticandastcenc-neon-static).x86_64only (so not forarm64)Test
On a Mac navigate to the root of this repository clone and run:
Verify SSE instructions:
Behavioral change
Due to a (presumably CMake) limitation the build only succeeds if
CMAKE_OSX_ARCHITECTURESis set explicitly toarm64;x86_64. Using$(ARCHS_STANDARD)leads to linking errors.Since before it did not compile at all, I consider this an acceptable improvement.
To reduce confusion I added an explicit configuration error that instructs users to use
arm64;x86_64explicitly. I prefer that option over silently overriding the value, which would eventually lead to future problems should Apple decide to change that standard arch set in the future.Not Addressed
I verified building universal iOS simulator binaries works, but have not checked whether the x86_64 arch has SSE enabled. I presume not, but do not consider this a blocker.
History
This picks up the previous attempt from #1043 , which was not accepted since the BasisU was built without SSE optimization in that one.