Skip to content

Commit bcdaedb

Browse files
abadamsclaudealexreinkinghalide-ci[bot]
authored
Revive the HelloiOS app (#9013)
* Revive reaction diffusion iOS app The xcode project still shells out to build the generators, which is not ideal. But there's a host-arch test file now that's built with cmake, so we can at least make sure the generators don't rot. * Redo the HelloiOS build * Remove HelloiOS from apps/CMakeLists.txt The HelloiOS build must run on macOS and be invoked twice. It isn't a superbuild, and we wouldn't want to inject a superbuild here anyway. * Move reaction_diffusion_2_generator.cpp to Generators directory * Single-target Xcode-driven prototype * use Xcode generator again * don't clutter the Xcode project navigator * Build for both device and simulator * Set folder directly in HalideGeneratorHelpers.cmake * Simplify folder setting in HelloiOS * A little more cleanup * Fix header selection * Use xcodebuild directly rather than store the CMake path * All supported iPhone versions have Metal * Rename build.sh to setup.sh * Update README.md * Generate xcconfig from CMake * Use XCode's own propagation mechanism for includes and libraries * Just bundle an XCFramework * Add helpers for creating iOS xcframeworks * Use new add_halide_xcframework helper * Get code-signing working * Don't inherit environment from device build * Drop env -i and patch HalideGeneratorHelpers.cmake instead * grammar tweak * Reflow troubleshooting paragraph * clang-format * Add note about known issue * Apply pre-commit auto-fixes * Add missing keep-sorted start * Pacify cmake linter * Apply pre-commit auto-fixes * Fix bad merge * Add a macOS workflow for HelloiOS testing * Use Python 3.10 so we don't need to build onnx wheels * Don't install to /opt * Use a more generic destination for iOS --------- Co-authored-by: Claude Code <noreply@anthropic.com> Co-authored-by: Alex Reinking <areinking@adobe.com> Co-authored-by: halide-ci[bot] <266445882+halide-ci[bot]@users.noreply.github.com>
1 parent d9733d1 commit bcdaedb

22 files changed

Lines changed: 1013 additions & 787 deletions
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: macOS
2+
3+
on:
4+
pull_request:
5+
types: [ opened, synchronize, reopened ]
6+
paths-ignore:
7+
- "doc/**"
8+
- "README.md"
9+
- "CODE_OF_CONDUCT.md"
10+
- "LICENSE.txt"
11+
- ".gitignore"
12+
- ".gitattributes"
13+
- ".gitmodules"
14+
- ".lldbinit"
15+
- ".github/**"
16+
- "!.github/workflows/testing-macos.yml"
17+
- "packaging/**"
18+
- "Makefile"
19+
- "Makefile.inc"
20+
- 'run-clang-tidy.sh'
21+
- '**.clang-tidy'
22+
23+
concurrency:
24+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
25+
cancel-in-progress: true
26+
27+
permissions:
28+
contents: read
29+
30+
jobs:
31+
macos:
32+
if: "!contains(github.event.pull_request.labels.*.name, 'skip_buildbots')"
33+
name: ${{ matrix.arch }} / ${{ matrix.uv_group }}
34+
runs-on: ${{ matrix.runner }}
35+
strategy:
36+
fail-fast: false
37+
matrix:
38+
# Disable extra cases until we have a fuller workflow
39+
# For now, we're only testing HelloiOS
40+
uv_group: [ "ci-llvm-main", ] # "ci-llvm-22", "ci-llvm-21"
41+
runner: [ "macos-26", ] # "macos-26-intel"
42+
include:
43+
- runner: macos-26
44+
arch: arm-64
45+
python: cpython-3.10.20-macos-aarch64-none
46+
47+
steps:
48+
- uses: actions/checkout@v4
49+
50+
- uses: astral-sh/setup-uv@v5
51+
52+
- name: Sync CI environment
53+
run: |
54+
uv sync --python '${{ matrix.python }}' --group '${{ matrix.uv_group }}' --no-install-project
55+
echo "${GITHUB_WORKSPACE}/.venv/bin" >> "$GITHUB_PATH"
56+
echo "VIRTUAL_ENV=${GITHUB_WORKSPACE}/.venv" >> "$GITHUB_ENV"
57+
58+
- name: Configure LLVM paths
59+
run: echo "Halide_LLVM_ROOT=$(halide-llvm --prefix)" >> "$GITHUB_ENV"
60+
61+
- name: Configure CMake
62+
run: >-
63+
cmake --preset ci-macos-${{ matrix.arch }}
64+
-DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install"
65+
-DWITH_TESTS=NO
66+
-DWITH_UTILS=NO
67+
-DWITH_TUTORIALS=NO
68+
-DWITH_PYTHON_BINDINGS=NO
69+
70+
- name: Initial build
71+
run: cmake --build build --target install
72+
73+
- name: Configure HelloiOS
74+
run: ./setup.sh
75+
working-directory: apps/HelloiOS
76+
env:
77+
Halide_ROOT: ${{ github.workspace }}/install
78+
79+
- name: Build HelloiOS
80+
run: >-
81+
xcodebuild -workspace HelloiOS.xcworkspace
82+
-scheme HelloiOS
83+
-configuration Debug
84+
-destination 'generic/platform=iOS Simulator'
85+
build
86+
working-directory: apps/HelloiOS

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ CMakeSettings.json
216216

217217
# XCode
218218
*.xcworkspacedata
219+
!apps/HelloiOS/HelloiOS.xcworkspace/contents.xcworkspacedata
219220
tools/objc/*.mobileprovision
220221
tools/objc/BUILD
221222
xcuserdata

apps/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ add_app(harris)
4747
# add_app(HelloAndroid) # TODO(#5374): missing CMake build
4848
# add_app(HelloAndroidCamera2) # TODO(#5374): missing CMake build
4949
add_app(HelloBaremetal)
50-
# add_app(HelloiOS) # TODO(#5374): missing CMake build
50+
# add_app(HelloiOS) # don't build HelloiOS here because it isn't universal.
5151
# add_app(HelloPyTorch) # TODO(#5374): missing CMake build
5252
add_app(hexagon_benchmarks)
5353
# add_app(hexagon_dma) # TODO(#5374): missing CMake build
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
cmake_minimum_required(VERSION 3.28)
2+
project(HelloiOS-Generators LANGUAGES C CXX)
3+
4+
# CMake hasn't yet finished implementing support for Xcode's new build system,
5+
# so disable the warning about it for now.
6+
set(CMAKE_XCODE_ATTRIBUTE_DISABLE_MANUAL_TARGET_ORDER_BUILD_WARNING YES)
7+
8+
set(CMAKE_CXX_STANDARD 17)
9+
set(CMAKE_CXX_STANDARD_REQUIRED YES)
10+
set(CMAKE_CXX_EXTENSIONS NO)
11+
12+
find_package(Halide REQUIRED)
13+
14+
add_halide_generator(
15+
reaction_diffusion_2_generator
16+
SOURCES reaction_diffusion_2_generator.cpp
17+
)
18+
19+
foreach (simulator IN ITEMS "" "-simulator")
20+
foreach (stage IN ITEMS init update render)
21+
add_halide_library(reaction_diffusion_2_${stage}${simulator}
22+
FROM reaction_diffusion_2_generator
23+
GENERATOR reaction_diffusion_2_${stage}
24+
FUNCTION_NAME reaction_diffusion_2_${stage}
25+
FILE_BASE_NAME reaction_diffusion_2_${stage}
26+
OUTPUT_DIR "arm-64-ios${simulator}"
27+
TARGETS "arm-64-ios${simulator}"
28+
FEATURES user_context)
29+
30+
add_halide_library(reaction_diffusion_2_metal_${stage}${simulator}
31+
FROM reaction_diffusion_2_generator
32+
GENERATOR reaction_diffusion_2_${stage}
33+
FUNCTION_NAME reaction_diffusion_2_metal_${stage}
34+
FILE_BASE_NAME reaction_diffusion_2_metal_${stage}
35+
OUTPUT_DIR "arm-64-ios${simulator}"
36+
TARGETS "arm-64-ios${simulator}"
37+
FEATURES metal user_context)
38+
endforeach ()
39+
endforeach ()
40+
41+
add_halide_xcframework(HalideKernels LIBRARIES ALL)

apps/HelloiOS/HelloiOS/reaction_diffusion_2_generator.cpp renamed to apps/HelloiOS/Generators/reaction_diffusion_2_generator.cpp

Lines changed: 18 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ class ReactionDiffusion2Update : public Halide::Generator<ReactionDiffusion2Upda
3535
Output<Buffer<float, 3>> new_state{"new_state"};
3636

3737
void generate() {
38-
clamped = Halide::BoundaryConditions::repeat_edge(state);
38+
// Add a boundary condition to the old state that treats out of bounds values as random
39+
clamped = Halide::BoundaryConditions::constant_exterior(state, random_float(frame));
40+
41+
// Diffusion
3942

4043
blur_x(x, y, c) = (clamped(x - 3, y, c) +
4144
clamped(x - 1, y, c) +
@@ -62,14 +65,16 @@ class ReactionDiffusion2Update : public Halide::Generator<ReactionDiffusion2Upda
6265
// Reaction
6366
Expr dR = B * (1 - R - G);
6467
Expr dG = (1 - B) * (R - G);
65-
Expr dB = 1 - B + 2 * G * R - R - G;
68+
Expr dB = 0.5f * (1 - B + 2 * G * R - R - G);
6669

67-
Expr bump = (frame % 1024) / 1024.0f;
68-
bump *= 1 - bump;
69-
Expr alpha = lerp(0.3f, 0.7f, bump);
70-
dR = select(dR > 0, dR * alpha, dR);
70+
// We'll massively speed up the reaction where the user touches
71+
Expr dx = x - mouse_x;
72+
Expr dy = y - mouse_y;
73+
Expr radius = dx * dx + dy * dy;
74+
Expr bump = 0.002f * state.dim(0).extent() * state.dim(1).extent() / max(1.0f, radius);
75+
bump = select(mouse_x >= 0, bump, 0.0f);
7176

72-
Expr t = 0.1f;
77+
Expr t = 0.04f + bump;
7378

7479
R += t * dR;
7580
G += t * dG;
@@ -80,26 +85,6 @@ class ReactionDiffusion2Update : public Halide::Generator<ReactionDiffusion2Upda
8085
B = clamp(B, 0.0f, 1.0f);
8186

8287
new_state(x, y, c) = mux(c, {R, G, B});
83-
84-
// Noise at the edges
85-
new_state(x, state.dim(1).min(), c) = random_float(frame) * 0.2f;
86-
new_state(x, state.dim(1).max(), c) = random_float(frame) * 0.2f;
87-
new_state(state.dim(0).min(), y, c) = random_float(frame) * 0.2f;
88-
new_state(state.dim(0).max(), y, c) = random_float(frame) * 0.2f;
89-
90-
// Add some white where the mouse is
91-
Expr min_x = clamp(mouse_x - 20, 0, state.dim(0).extent() - 1);
92-
Expr max_x = clamp(mouse_x + 20, 0, state.dim(0).extent() - 1);
93-
Expr min_y = clamp(mouse_y - 20, 0, state.dim(1).extent() - 1);
94-
Expr max_y = clamp(mouse_y + 20, 0, state.dim(1).extent() - 1);
95-
clobber = RDom(min_x, max_x - min_x + 1, min_y, max_y - min_y + 1);
96-
97-
Expr dx = clobber.x - mouse_x;
98-
Expr dy = clobber.y - mouse_y;
99-
Expr radius = dx * dx + dy * dy;
100-
new_state(clobber.x, clobber.y, c) = select(radius < 400.0f,
101-
1.0f,
102-
new_state(clobber.x, clobber.y, c));
10388
}
10489

10590
void schedule() {
@@ -112,53 +97,33 @@ class ReactionDiffusion2Update : public Halide::Generator<ReactionDiffusion2Upda
11297
if (get_target().has_gpu_feature()) {
11398
blur
11499
.reorder(c, x, y)
115-
.vectorize(c)
100+
.unroll(c)
116101
.compute_at(new_state, xi);
117102

118103
new_state.gpu_tile(x, y, xi, yi, 8, 2);
119104

120-
for (int i = 0; i <= 1; ++i) {
121-
new_state.update(i)
122-
.reorder(c, x)
123-
.unroll(c)
124-
.gpu_tile(x, xi, 8);
125-
}
126-
for (int i = 2; i <= 3; ++i) {
127-
new_state.update(i)
128-
.reorder(c, y)
129-
.unroll(c)
130-
.gpu_tile(y, yi, 8);
131-
}
132-
new_state.update(4)
133-
.reorder(c, clobber.x)
134-
.unroll(c)
135-
.gpu_tile(clobber.x, clobber.y, 1, 1);
136-
137105
state.dim(0).set_stride(3);
138106
state.dim(2).set_stride(1).set_extent(3);
139107
new_state.dim(0).set_stride(3);
140108
new_state.dim(2).set_stride(1).set_extent(3);
141109
} else {
142110
Var yi;
143111
new_state
144-
.split(y, y, yi, 64)
112+
.split(y, y, yi, 32)
145113
.parallel(y)
146114
.vectorize(x, natural_vector_size<float>());
147115

148-
blur
149-
.compute_at(new_state, yi)
150-
.vectorize(x, natural_vector_size<float>());
151-
152116
clamped
117+
.store_in(MemoryType::Stack)
153118
.store_at(new_state, y)
154-
.compute_at(new_state, yi);
119+
.compute_at(new_state, yi)
120+
.vectorize(Halide::_0, natural_vector_size<float>());
155121
}
156122
}
157123

158124
private:
159125
Func blur_x, blur_y, blur, clamped;
160126
Var x, y, xi, yi, c;
161-
RDom clobber;
162127
};
163128

164129
class ReactionDiffusion2Render : public Halide::Generator<ReactionDiffusion2Render> {
@@ -170,7 +135,7 @@ class ReactionDiffusion2Render : public Halide::Generator<ReactionDiffusion2Rend
170135

171136
void generate() {
172137
Func contour;
173-
contour(x, y, c) = pow(state(x, y, c) * (1 - state(x, y, c)) * 4, 8);
138+
contour(x, y, c) = pow(state(x, y, c) * (1 - state(x, y, c)) * 4, 2);
174139

175140
Expr c0 = contour(x, y, 0);
176141
Expr c1 = contour(x, y, 1);

0 commit comments

Comments
 (0)