diff --git a/.github/workflows/testing-macos.yml b/.github/workflows/testing-macos.yml new file mode 100644 index 000000000000..2578bb0e3134 --- /dev/null +++ b/.github/workflows/testing-macos.yml @@ -0,0 +1,86 @@ +name: macOS + +on: + pull_request: + types: [ opened, synchronize, reopened ] + paths-ignore: + - "doc/**" + - "README.md" + - "CODE_OF_CONDUCT.md" + - "LICENSE.txt" + - ".gitignore" + - ".gitattributes" + - ".gitmodules" + - ".lldbinit" + - ".github/**" + - "!.github/workflows/testing-macos.yml" + - "packaging/**" + - "Makefile" + - "Makefile.inc" + - 'run-clang-tidy.sh' + - '**.clang-tidy' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + macos: + if: "!contains(github.event.pull_request.labels.*.name, 'skip_buildbots')" + name: ${{ matrix.arch }} / ${{ matrix.uv_group }} + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + # Disable extra cases until we have a fuller workflow + # For now, we're only testing HelloiOS + uv_group: [ "ci-llvm-main", ] # "ci-llvm-22", "ci-llvm-21" + runner: [ "macos-26", ] # "macos-26-intel" + include: + - runner: macos-26 + arch: arm-64 + python: cpython-3.10.20-macos-aarch64-none + + steps: + - uses: actions/checkout@v4 + + - uses: astral-sh/setup-uv@v5 + + - name: Sync CI environment + run: | + uv sync --python '${{ matrix.python }}' --group '${{ matrix.uv_group }}' --no-install-project + echo "${GITHUB_WORKSPACE}/.venv/bin" >> "$GITHUB_PATH" + echo "VIRTUAL_ENV=${GITHUB_WORKSPACE}/.venv" >> "$GITHUB_ENV" + + - name: Configure LLVM paths + run: echo "Halide_LLVM_ROOT=$(halide-llvm --prefix)" >> "$GITHUB_ENV" + + - name: Configure CMake + run: >- + cmake --preset ci-macos-${{ matrix.arch }} + -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" + -DWITH_TESTS=NO + -DWITH_UTILS=NO + -DWITH_TUTORIALS=NO + -DWITH_PYTHON_BINDINGS=NO + + - name: Initial build + run: cmake --build build --target install + + - name: Configure HelloiOS + run: ./setup.sh + working-directory: apps/HelloiOS + env: + Halide_ROOT: ${{ github.workspace }}/install + + - name: Build HelloiOS + run: >- + xcodebuild -workspace HelloiOS.xcworkspace + -scheme HelloiOS + -configuration Debug + -destination 'generic/platform=iOS Simulator' + build + working-directory: apps/HelloiOS diff --git a/.gitignore b/.gitignore index a08b8e8dd7f3..368a457c85c2 100644 --- a/.gitignore +++ b/.gitignore @@ -216,6 +216,7 @@ CMakeSettings.json # XCode *.xcworkspacedata +!apps/HelloiOS/HelloiOS.xcworkspace/contents.xcworkspacedata tools/objc/*.mobileprovision tools/objc/BUILD xcuserdata diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 2f2213aa087a..4702e6c5fa32 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -47,7 +47,7 @@ add_app(harris) # add_app(HelloAndroid) # TODO(#5374): missing CMake build # add_app(HelloAndroidCamera2) # TODO(#5374): missing CMake build add_app(HelloBaremetal) -# add_app(HelloiOS) # TODO(#5374): missing CMake build +# add_app(HelloiOS) # don't build HelloiOS here because it isn't universal. # add_app(HelloPyTorch) # TODO(#5374): missing CMake build add_app(hexagon_benchmarks) # add_app(hexagon_dma) # TODO(#5374): missing CMake build diff --git a/apps/HelloiOS/Generators/CMakeLists.txt b/apps/HelloiOS/Generators/CMakeLists.txt new file mode 100644 index 000000000000..0f3b1e36798b --- /dev/null +++ b/apps/HelloiOS/Generators/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.28) +project(HelloiOS-Generators LANGUAGES C CXX) + +# CMake hasn't yet finished implementing support for Xcode's new build system, +# so disable the warning about it for now. +set(CMAKE_XCODE_ATTRIBUTE_DISABLE_MANUAL_TARGET_ORDER_BUILD_WARNING YES) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_CXX_EXTENSIONS NO) + +find_package(Halide REQUIRED) + +add_halide_generator( + reaction_diffusion_2_generator + SOURCES reaction_diffusion_2_generator.cpp +) + +foreach (simulator IN ITEMS "" "-simulator") + foreach (stage IN ITEMS init update render) + add_halide_library(reaction_diffusion_2_${stage}${simulator} + FROM reaction_diffusion_2_generator + GENERATOR reaction_diffusion_2_${stage} + FUNCTION_NAME reaction_diffusion_2_${stage} + FILE_BASE_NAME reaction_diffusion_2_${stage} + OUTPUT_DIR "arm-64-ios${simulator}" + TARGETS "arm-64-ios${simulator}" + FEATURES user_context) + + add_halide_library(reaction_diffusion_2_metal_${stage}${simulator} + FROM reaction_diffusion_2_generator + GENERATOR reaction_diffusion_2_${stage} + FUNCTION_NAME reaction_diffusion_2_metal_${stage} + FILE_BASE_NAME reaction_diffusion_2_metal_${stage} + OUTPUT_DIR "arm-64-ios${simulator}" + TARGETS "arm-64-ios${simulator}" + FEATURES metal user_context) + endforeach () +endforeach () + +add_halide_xcframework(HalideKernels LIBRARIES ALL) diff --git a/apps/HelloiOS/HelloiOS/reaction_diffusion_2_generator.cpp b/apps/HelloiOS/Generators/reaction_diffusion_2_generator.cpp similarity index 72% rename from apps/HelloiOS/HelloiOS/reaction_diffusion_2_generator.cpp rename to apps/HelloiOS/Generators/reaction_diffusion_2_generator.cpp index 308ef39344f9..6de5657bbdcc 100644 --- a/apps/HelloiOS/HelloiOS/reaction_diffusion_2_generator.cpp +++ b/apps/HelloiOS/Generators/reaction_diffusion_2_generator.cpp @@ -35,7 +35,10 @@ class ReactionDiffusion2Update : public Halide::Generator> new_state{"new_state"}; void generate() { - clamped = Halide::BoundaryConditions::repeat_edge(state); + // Add a boundary condition to the old state that treats out of bounds values as random + clamped = Halide::BoundaryConditions::constant_exterior(state, random_float(frame)); + + // Diffusion blur_x(x, y, c) = (clamped(x - 3, y, c) + clamped(x - 1, y, c) + @@ -62,14 +65,16 @@ class ReactionDiffusion2Update : public Halide::Generator 0, dR * alpha, dR); + // We'll massively speed up the reaction where the user touches + Expr dx = x - mouse_x; + Expr dy = y - mouse_y; + Expr radius = dx * dx + dy * dy; + Expr bump = 0.002f * state.dim(0).extent() * state.dim(1).extent() / max(1.0f, radius); + bump = select(mouse_x >= 0, bump, 0.0f); - Expr t = 0.1f; + Expr t = 0.04f + bump; R += t * dR; G += t * dG; @@ -80,26 +85,6 @@ class ReactionDiffusion2Update : public Halide::Generator()); - blur - .compute_at(new_state, yi) - .vectorize(x, natural_vector_size()); - clamped + .store_in(MemoryType::Stack) .store_at(new_state, y) - .compute_at(new_state, yi); + .compute_at(new_state, yi) + .vectorize(Halide::_0, natural_vector_size()); } } private: Func blur_x, blur_y, blur, clamped; Var x, y, xi, yi, c; - RDom clobber; }; class ReactionDiffusion2Render : public Halide::Generator { @@ -170,7 +135,7 @@ class ReactionDiffusion2Render : public Halide::Generator + + + + + + diff --git a/apps/HelloiOS/HelloiOS/AppDelegate.mm b/apps/HelloiOS/HelloiOS/AppDelegate.mm index 8934c9d8f3dd..3da73d7fa2d8 100644 --- a/apps/HelloiOS/HelloiOS/AppDelegate.mm +++ b/apps/HelloiOS/HelloiOS/AppDelegate.mm @@ -1,82 +1,21 @@ #import "AppDelegate.h" -#import "HalideView.h" -#import "HalideViewController.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - - HalideViewController *halide_view_controller = [[HalideViewController alloc] init]; - - self.window.rootViewController = halide_view_controller; - - const int kBorderX = 10; - const int kBorderY = 40; - - // Add a view for image output - int image_width, image_height; - HalideView *output_image = [HalideView alloc]; - { - CGRect box = self.window.frame; - box.origin.x += kBorderX; - box.origin.y += kBorderY; - box.size.width -= kBorderX * 2; - box.size.height -= kBorderY * 2; - image_width = box.size.width; - image_height = box.size.height; - output_image = [output_image initWithFrame:box]; - output_image.backgroundColor = [UIColor blackColor]; - [self.window addSubview:output_image]; - [output_image setUserInteractionEnabled:true]; -#if HAS_METAL_SDK - output_image.use_metal = true; -#endif - } - halide_view_controller.halide_view = output_image; - - // Add a view for text output - UITextView *output_log = [UITextView alloc]; - { - CGRect box = self.window.frame; - box.origin.x += kBorderX; - box.origin.y += kBorderY + image_height; - box.size.width -= box.origin.x + kBorderX; - box.size.height -= image_height + kBorderY; - output_log = [output_log initWithFrame:box]; - UIFont *font = [UIFont systemFontOfSize:20]; - [output_log setFont:font]; - [self.window addSubview:output_log]; - } - output_image.outputLog = output_log; - - // Override point for customization after application launch. - self.window.backgroundColor = [UIColor whiteColor]; - [self.window makeKeyAndVisible]; - return YES; } -- (void)applicationWillResignActive:(UIApplication *)application { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. -} - -- (void)applicationDidEnterBackground:(UIApplication *)application { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. -} - -- (void)applicationWillEnterForeground:(UIApplication *)application { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. -} - -- (void)applicationDidBecomeActive:(UIApplication *)application { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. +- (UISceneConfiguration *)application:(UIApplication *)application + configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession + options:(UISceneConnectionOptions *)options { + UISceneConfiguration *configuration = [[UISceneConfiguration alloc] initWithName:@"Default Configuration" + sessionRole:connectingSceneSession.role]; + configuration.delegateClass = NSClassFromString(@"SceneDelegate"); + return configuration; } -- (void)applicationWillTerminate:(UIApplication *)application { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { } @end diff --git a/apps/HelloiOS/HelloiOS/HalideView.h b/apps/HelloiOS/HelloiOS/HalideView.h index de4506bb7dda..5d9c79cb9f73 100644 --- a/apps/HelloiOS/HelloiOS/HalideView.h +++ b/apps/HelloiOS/HelloiOS/HalideView.h @@ -1,35 +1,18 @@ -#include - -#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0 && !(TARGET_IPHONE_SIMULATOR || TARGET_OS_SIMULATOR) -#define HAS_METAL_SDK 1 -#else -#define HAS_METAL_SDK 0 -#endif - +#import #import #import -#if HAS_METAL_SDK -#import -#endif // HAS_METAL_SDK - -#if HAS_METAL_SDK @interface HalideView : UIView -#else -@interface HalideView : UIImageView -#endif @property CGPoint touch_position; @property bool touch_active; -@property UITextView *outputLog; +@property UILabel *statsLabel; -#if HAS_METAL_SDK @property bool use_metal; // view has a handle to the metal device when created @property(nonatomic, readonly) id device; // view has a handle to the metal device when created @property(nonatomic, readonly) id commandQueue; -#endif // HAS_METAL_SDK - (void)initiateRender; diff --git a/apps/HelloiOS/HelloiOS/HalideView.mm b/apps/HelloiOS/HelloiOS/HalideView.mm index 683714d1cb05..42b989edb459 100644 --- a/apps/HelloiOS/HelloiOS/HalideView.mm +++ b/apps/HelloiOS/HelloiOS/HalideView.mm @@ -3,14 +3,14 @@ #include "HalideBuffer.h" #include "HalideRuntime.h" #include "HalideRuntimeMetal.h" + #include "reaction_diffusion_2_init.h" #include "reaction_diffusion_2_render.h" #include "reaction_diffusion_2_update.h" -#if HAS_METAL_SDK + #include "reaction_diffusion_2_metal_init.h" #include "reaction_diffusion_2_metal_render.h" #include "reaction_diffusion_2_metal_update.h" -#endif using Halide::Runtime::Buffer; @@ -25,18 +25,14 @@ reaction_diffusion_2_update, reaction_diffusion_2_render}; -#if HAS_METAL_SDK static const HalideFuncs kHalideMetal = { reaction_diffusion_2_metal_init, reaction_diffusion_2_metal_update, reaction_diffusion_2_metal_render}; -#endif @implementation HalideView { @private -#if HAS_METAL_SDK __weak CAMetalLayer *_metalLayer; -#endif // HAS_METAL_SDK Buffer buf1; Buffer buf2; @@ -46,17 +42,15 @@ @implementation HalideView { double frameElapsedEstimate; } -#if HAS_METAL_SDK + (Class)layerClass { return [CAMetalLayer class]; } -#endif // HAS_METAL_SDK - (void)initCommon { self.opaque = YES; self.backgroundColor = nil; + self.userInteractionEnabled = YES; -#if HAS_METAL_SDK _metalLayer = (CAMetalLayer *)self.layer; _metalLayer.delegate = self; @@ -67,7 +61,6 @@ - (void)initCommon { _metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; _metalLayer.framebufferOnly = NO; -#endif // HAS_METAL_SDK } - (void)resetFrameTime { @@ -84,7 +77,7 @@ - (void)updateFrameTime:(double)elapsed using_metal:(bool)using_metal { } if ((iteration % 30) == 0) { dispatch_async(dispatch_get_main_queue(), ^(void) { - [self updateLogWith:frameElapsedEstimate using_metal:using_metal]; + [self updateLogWith:self->frameElapsedEstimate using_metal:using_metal]; }); } iteration += 1; @@ -106,7 +99,7 @@ - (void)initBufsWithWidth:(int)w height:(int)h using_metal:(bool)using_metal { const int pad_pixels = (64 / sizeof(int32_t)); const int row_stride = (w + pad_pixels - 1) & ~(pad_pixels - 1); const halide_dimension_t pixelBufShape[] = { - {0, w, c}, + {0, row_stride, c}, {0, h, c * row_stride}, {0, c, 1}}; @@ -114,6 +107,9 @@ - (void)initBufsWithWidth:(int)w height:(int)h using_metal:(bool)using_metal { // and memory managed by Buffer itself pixel_buf = Buffer(nullptr, 3, pixelBufShape); pixel_buf.allocate(); + // the blit command requires the padding to exist even on the last row, + // so we allocate it padded then crop. + pixel_buf.crop(0, 0, w); } - (void)didMoveToWindow { @@ -138,26 +134,19 @@ - (instancetype)initWithCoder:(NSCoder *)coder { } - (void)updateLogWith:(double)elapsedTime using_metal:(bool)using_metal { -#if HAS_METAL_SDK - NSString *mode = using_metal ? @"(Metal; Double-tap for CPU)" : @"(CPU; Double-tap for Metal)"; -#else - NSString *mode = @"(CPU; Metal not available)"; -#endif - [self.outputLog setText:[NSString stringWithFormat:@"Halide routine takes %0.3f ms %@", - elapsedTime * 1000, mode]]; + NSString *mode = using_metal ? @"Metal (double-tap for CPU)" : @"CPU (double-tap for Metal)"; + self.statsLabel.text = [NSString stringWithFormat:@"%.1f ms • %@", elapsedTime * 1000, mode]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; self.touch_position = [touch locationInView:self]; self.touch_active = [self pointInside:self.touch_position withEvent:event]; -#if HAS_METAL_SDK NSUInteger numTaps = [touch tapCount]; if (numTaps > 1) { self.use_metal = !self.use_metal; NSLog(@"TBTaps: %d, self.use_metal %d", (int)numTaps, (int)self.use_metal); } -#endif } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { @@ -174,7 +163,8 @@ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { } - (void)renderOneFrame:(const HalideFuncs &)halide_funcs using_metal:(bool)using_metal { - int tx = -100, ty = -100; + // -1 is interpreted as no touch event + int tx = -1, ty = -1; if (self.touch_active) { // Note that buf/bounds is not necessarily the same as self.contentScaleFactor tx = (int)(self.touch_position.x * buf1.dim(0).extent() / self.bounds.size.width); @@ -182,11 +172,7 @@ - (void)renderOneFrame:(const HalideFuncs &)halide_funcs using_metal:(bool)using NSLog(@"touch %d %d", tx, ty); } -#if HAS_METAL_SDK const bool output_bgra = true; -#else - const bool output_bgra = false; -#endif // A note on timing: based on our experimentation, this is indeed effective for // timing Metal launches, not just CPU kernels. Other GPU API implementations @@ -211,7 +197,6 @@ - (void)renderOneFrame:(const HalideFuncs &)halide_funcs using_metal:(bool)using } - (void)initiateRender { -#if HAS_METAL_SDK bool using_metal = self.use_metal; const HalideFuncs &halide_funcs = using_metal ? kHalideMetal : kHalideCPU; const int required_stride = using_metal ? 3 : 1; @@ -223,17 +208,13 @@ - (void)initiateRender { id texture = drawable.texture; // handle display changes here - if (texture.width != buf1.dim(0).extent() || + if (!buf1.data() || + texture.width != buf1.dim(0).extent() || texture.height != buf1.dim(1).extent() || buf1.dim(0).stride() != required_stride) { // set the metal layer to the drawable size in case orientation or size changes CGSize drawableSize = self.bounds.size; - - // The Metal schedule for our Halide code requires that - // the image be exact multiples of 8 in x & y - drawableSize.width = ((long)drawableSize.width + 7) & ~7; - drawableSize.height = ((long)drawableSize.height + 7) & ~7; _metalLayer.drawableSize = drawableSize; [self initBufsWithWidth:drawableSize.width height:drawableSize.height using_metal:using_metal]; @@ -244,6 +225,8 @@ - (void)initiateRender { [self renderOneFrame:halide_funcs using_metal:using_metal]; + [self renderOneFrame:halide_funcs using_metal:using_metal]; + id buffer = using_metal ? (__bridge id)(void *)halide_metal_get_buffer((void *)&self, pixel_buf) : [self.device newBufferWithBytes:pixel_buf.data() @@ -263,7 +246,7 @@ - (void)initiateRender { copyFromBuffer:buffer sourceOffset:0 sourceBytesPerRow:bytesPerRow - sourceBytesPerImage:pixel_buf.size_in_bytes() + sourceBytesPerImage:pixel_buf.dim(1).stride() * pixel_buf.dim(1).extent() // Not sizeInBytes because we're also copying the padding on the last row sourceSize:image_size toTexture:drawable.texture destinationSlice:0 @@ -277,52 +260,11 @@ - (void)initiateRender { }); }]; [commandBuffer commit]; - [_commandQueue insertDebugCaptureBoundary]; } -#else - float f = self.contentScaleFactor; - int image_width = (int)(self.bounds.size.width * f); - int image_height = (int)(self.bounds.size.height * f); - const HalideFuncs &halide_funcs = kHalideCPU; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self initBufsWithWidth:image_width height:image_height using_metal:false]; - - CGDataProviderRef provider = - CGDataProviderCreateWithData(NULL, pixel_buf.data(), pixel_buf.size_in_bytes(), NULL); - CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB(); - - halide_funcs.init((__bridge void *)self, buf1); - [self resetFrameTime]; - - for (;;) { - [self renderOneFrame:halide_funcs using_metal:false]; - - const int bytesPerRow = pixel_buf.dim(1).stride() * pixel_buf.type().bits / 8; - CGImageRef image_ref = - CGImageCreate(image_width, image_height, - 8, // bitsPerComponent - 32, // bitsPerPixel - bytesPerRow, - color_space, - kCGBitmapByteOrderDefault, - provider, NULL, NO, - kCGRenderingIntentDefault); - UIImage *im = [UIImage imageWithCGImage:image_ref]; - CGImageRelease(image_ref); - - dispatch_async(dispatch_get_main_queue(), ^(void) { - [self setImage:im]; - }); - } - }); -#endif // HAS_METAL_SDK } @end -#if HAS_METAL_SDK - #ifdef __cplusplus extern "C" { #endif @@ -342,5 +284,3 @@ int halide_metal_release_context(void *user_context) { #ifdef __cplusplus } #endif - -#endif // HAS_METAL_SDK diff --git a/apps/HelloiOS/HelloiOS/HalideViewController.h b/apps/HelloiOS/HelloiOS/HalideViewController.h index 633f8d3fab7c..623acdbfc5c8 100644 --- a/apps/HelloiOS/HelloiOS/HalideViewController.h +++ b/apps/HelloiOS/HelloiOS/HalideViewController.h @@ -1,15 +1,4 @@ -#ifndef HelloiOS_HalideViewController_h -#define HelloiOS_HalideViewController_h - -#import "HalideView.h" #import @interface HalideViewController : UIViewController - -@property HalideView *halide_view; - -- (void)viewWillAppear:(BOOL)animated; - @end - -#endif diff --git a/apps/HelloiOS/HelloiOS/HalideViewController.mm b/apps/HelloiOS/HelloiOS/HalideViewController.mm index a1102a466e0f..a6d41cd4b51a 100644 --- a/apps/HelloiOS/HelloiOS/HalideViewController.mm +++ b/apps/HelloiOS/HelloiOS/HalideViewController.mm @@ -1,30 +1,45 @@ #import "HalideViewController.h" #import "HalideView.h" -#import @implementation HalideViewController { - HalideView *_halide_view; + HalideView *_halideView; + UILabel *_statsLabel; } -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - [_halide_view initiateRender]; -} +- (void)loadView { + // Create the HalideView as the main view + _halideView = [[HalideView alloc] initWithFrame:CGRectZero]; + _halideView.backgroundColor = [UIColor blackColor]; -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - [_halide_view touchesBegan:touches withEvent:event]; -} + _halideView.use_metal = YES; -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - [_halide_view touchesMoved:touches withEvent:event]; -} + self.view = _halideView; -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - [_halide_view touchesEnded:touches withEvent:event]; + // Add stats label at the bottom + _statsLabel = [[UILabel alloc] init]; + _statsLabel.textColor = [UIColor whiteColor]; + _statsLabel.font = [UIFont systemFontOfSize:14]; + _statsLabel.numberOfLines = 0; + _statsLabel.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:_statsLabel]; + + // Pin label to bottom with safe area + [NSLayoutConstraint activateConstraints:@[ + [_statsLabel.leadingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor + constant:10], + [_statsLabel.trailingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor + constant:-10], + [_statsLabel.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor + constant:-10] + ]]; + + // Give HalideView a reference to the label for updates + _halideView.statsLabel = _statsLabel; } -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - [_halide_view touchesCancelled:touches withEvent:event]; +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [_halideView initiateRender]; } @end diff --git a/apps/HelloiOS/HelloiOS/HelloiOS-Info.plist b/apps/HelloiOS/HelloiOS/HelloiOS-Info.plist index f407f17fd556..2fd88cd2fd3a 100644 --- a/apps/HelloiOS/HelloiOS/HelloiOS-Info.plist +++ b/apps/HelloiOS/HelloiOS/HelloiOS-Info.plist @@ -5,15 +5,15 @@ CFBundleDevelopmentRegion en CFBundleDisplayName - ${PRODUCT_NAME} + $(PRODUCT_NAME) CFBundleExecutable - ${EXECUTABLE_NAME} + $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName - ${PRODUCT_NAME} + $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString @@ -24,6 +24,34 @@ 1.0 LSRequiresIPhoneOS + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + SceneDelegate + + + + + UILaunchScreen + + UIColorName + + UIImageName + + + UIRequiredDeviceCapabilities + + arm64 + UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/apps/HelloiOS/HelloiOS/HelloiOS-Prefix.pch b/apps/HelloiOS/HelloiOS/HelloiOS-Prefix.pch index 743435c9bee5..3fdee9d3d6b7 100644 --- a/apps/HelloiOS/HelloiOS/HelloiOS-Prefix.pch +++ b/apps/HelloiOS/HelloiOS/HelloiOS-Prefix.pch @@ -4,12 +4,6 @@ // The contents of this file are implicitly included at the beginning of every source file. // -#import - -#ifndef __IPHONE_3_0 -#warning "This project uses features only available in iOS SDK 3.0 and later." -#endif - #ifdef __OBJC__ #import #import diff --git a/apps/HelloiOS/HelloiOS/HelloiOS.xcodeproj/project.pbxproj b/apps/HelloiOS/HelloiOS/HelloiOS.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..c40c33190623 --- /dev/null +++ b/apps/HelloiOS/HelloiOS/HelloiOS.xcodeproj/project.pbxproj @@ -0,0 +1,365 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 4A2000000000000000000001 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A1000000000000000000002 /* AppDelegate.mm */; }; + 4A2000000000000000000002 /* HalideView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A1000000000000000000004 /* HalideView.mm */; }; + 4A2000000000000000000003 /* HalideViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A1000000000000000000006 /* HalideViewController.mm */; }; + 4A2000000000000000000004 /* SceneDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A1000000000000000000008 /* SceneDelegate.mm */; }; + 4A2000000000000000000005 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A1000000000000000000009 /* main.mm */; }; + 4A2000000000000000000006 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4A100000000000000000000A /* Images.xcassets */; }; + 4A2000000000000000000007 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4A100000000000000000000B /* InfoPlist.strings */; }; + 4A2000000000000000000008 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A100000000000000000000F /* CoreGraphics.framework */; }; + 4A2000000000000000000009 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A1000000000000000000010 /* Foundation.framework */; }; + 4A200000000000000000000A /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A1000000000000000000011 /* Metal.framework */; }; + 4A200000000000000000000B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A1000000000000000000012 /* QuartzCore.framework */; }; + 4A200000000000000000000C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A1000000000000000000013 /* UIKit.framework */; }; + 4A200000000000000000000D /* HalideKernels.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A1000000000000000000015 /* HalideKernels.xcframework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 4A1000000000000000000001 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 4A1000000000000000000002 /* AppDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AppDelegate.mm; sourceTree = ""; }; + 4A1000000000000000000003 /* HalideView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HalideView.h; sourceTree = ""; }; + 4A1000000000000000000004 /* HalideView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HalideView.mm; sourceTree = ""; }; + 4A1000000000000000000005 /* HalideViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HalideViewController.h; sourceTree = ""; }; + 4A1000000000000000000006 /* HalideViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HalideViewController.mm; sourceTree = ""; }; + 4A1000000000000000000007 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; + 4A1000000000000000000008 /* SceneDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SceneDelegate.mm; sourceTree = ""; }; + 4A1000000000000000000009 /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; + 4A100000000000000000000A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 4A100000000000000000000B /* InfoPlist.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = InfoPlist.strings; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 4A100000000000000000000C /* HelloiOS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HelloiOS-Info.plist"; sourceTree = ""; }; + 4A100000000000000000000D /* HelloiOS-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "HelloiOS-Prefix.pch"; sourceTree = ""; }; + 4A100000000000000000000F /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 4A1000000000000000000010 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 4A1000000000000000000011 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; + 4A1000000000000000000012 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 4A1000000000000000000013 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 4A1000000000000000000014 /* HelloiOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloiOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4A1000000000000000000015 /* HalideKernels.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = HalideKernels.xcframework; path = ../Generators/build/HalideKernels.xcframework; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4A4000000000000000000002 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4A2000000000000000000008 /* CoreGraphics.framework in Frameworks */, + 4A2000000000000000000009 /* Foundation.framework in Frameworks */, + 4A200000000000000000000A /* Metal.framework in Frameworks */, + 4A200000000000000000000B /* QuartzCore.framework in Frameworks */, + 4A200000000000000000000C /* UIKit.framework in Frameworks */, + 4A200000000000000000000D /* HalideKernels.xcframework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4A3000000000000000000001 = { + isa = PBXGroup; + children = ( + 4A1000000000000000000001 /* AppDelegate.h */, + 4A1000000000000000000002 /* AppDelegate.mm */, + 4A1000000000000000000003 /* HalideView.h */, + 4A1000000000000000000004 /* HalideView.mm */, + 4A1000000000000000000005 /* HalideViewController.h */, + 4A1000000000000000000006 /* HalideViewController.mm */, + 4A1000000000000000000007 /* SceneDelegate.h */, + 4A1000000000000000000008 /* SceneDelegate.mm */, + 4A1000000000000000000009 /* main.mm */, + 4A100000000000000000000A /* Images.xcassets */, + 4A100000000000000000000B /* InfoPlist.strings */, + 4A100000000000000000000C /* HelloiOS-Info.plist */, + 4A100000000000000000000D /* HelloiOS-Prefix.pch */, + 4A3000000000000000000003 /* Frameworks */, + 4A3000000000000000000002 /* Products */, + ); + sourceTree = ""; + }; + 4A3000000000000000000002 /* Products */ = { + isa = PBXGroup; + children = ( + 4A1000000000000000000014 /* HelloiOS.app */, + ); + name = Products; + sourceTree = ""; + }; + 4A3000000000000000000003 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4A100000000000000000000F /* CoreGraphics.framework */, + 4A1000000000000000000010 /* Foundation.framework */, + 4A1000000000000000000011 /* Metal.framework */, + 4A1000000000000000000012 /* QuartzCore.framework */, + 4A1000000000000000000013 /* UIKit.framework */, + 4A1000000000000000000015 /* HalideKernels.xcframework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4A5000000000000000000001 /* HelloiOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4A7000000000000000000002 /* Build configuration list for PBXNativeTarget "HelloiOS" */; + buildPhases = ( + 4A4000000000000000000004 /* Build Halide Generators */, + 4A4000000000000000000001 /* Sources */, + 4A4000000000000000000002 /* Frameworks */, + 4A4000000000000000000003 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HelloiOS; + productName = HelloiOS; + productReference = 4A1000000000000000000014 /* HelloiOS.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4A6000000000000000000001 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 2630; + }; + buildConfigurationList = 4A7000000000000000000001 /* Build configuration list for PBXProject "HelloiOS" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 4A3000000000000000000001; + productRefGroup = 4A3000000000000000000002 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4A5000000000000000000001 /* HelloiOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 4A4000000000000000000003 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4A2000000000000000000006 /* Images.xcassets in Resources */, + 4A2000000000000000000007 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 4A4000000000000000000004 /* Build Halide Generators */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Build Halide Generators"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "xcodebuild -project \"${PROJECT_DIR}/../Generators/build/HelloiOS-Generators.xcodeproj\" \\\n -target HalideKernels \\\n -configuration Release\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4A4000000000000000000001 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4A2000000000000000000001 /* AppDelegate.mm in Sources */, + 4A2000000000000000000002 /* HalideView.mm in Sources */, + 4A2000000000000000000003 /* HalideViewController.mm in Sources */, + 4A2000000000000000000004 /* SceneDelegate.mm in Sources */, + 4A2000000000000000000005 /* main.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 4A8000000000000000000001 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + }; + name = Debug; + }; + 4A8000000000000000000002 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 4A8000000000000000000003 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_ENABLE_OBJC_ARC = YES; + CODE_SIGN_IDENTITY = "-"; + ENABLE_BITCODE = NO; + EXCLUDED_ARCHS = x86_64; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HelloiOS-Prefix.pch"; + INFOPLIST_FILE = "HelloiOS-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + PRODUCT_BUNDLE_IDENTIFIER = org.halide.HelloiOS; + PRODUCT_NAME = HelloiOS; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 4A8000000000000000000004 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_ENABLE_OBJC_ARC = YES; + CODE_SIGN_IDENTITY = "-"; + ENABLE_BITCODE = NO; + EXCLUDED_ARCHS = x86_64; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HelloiOS-Prefix.pch"; + INFOPLIST_FILE = "HelloiOS-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + PRODUCT_BUNDLE_IDENTIFIER = org.halide.HelloiOS; + PRODUCT_NAME = HelloiOS; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4A7000000000000000000001 /* Build configuration list for PBXProject "HelloiOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4A8000000000000000000001 /* Debug */, + 4A8000000000000000000002 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4A7000000000000000000002 /* Build configuration list for PBXNativeTarget "HelloiOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4A8000000000000000000003 /* Debug */, + 4A8000000000000000000004 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4A6000000000000000000001 /* Project object */; +} diff --git a/apps/HelloiOS/HelloiOS/Images.xcassets/AppIcon.appiconset/Contents.json b/apps/HelloiOS/HelloiOS/Images.xcassets/AppIcon.appiconset/Contents.json index d7070bc5c02a..9221b9bb1a35 100644 --- a/apps/HelloiOS/HelloiOS/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/apps/HelloiOS/HelloiOS/Images.xcassets/AppIcon.appiconset/Contents.json @@ -2,92 +2,97 @@ "images" : [ { "idiom" : "iphone", - "size" : "20x20", - "scale" : "2x" + "scale" : "2x", + "size" : "20x20" }, { "idiom" : "iphone", - "size" : "20x20", - "scale" : "3x" + "scale" : "3x", + "size" : "20x20" }, { "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" + "scale" : "2x", + "size" : "29x29" }, { "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" + "scale" : "3x", + "size" : "29x29" }, { "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" + "scale" : "2x", + "size" : "40x40" }, { "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" + "scale" : "3x", + "size" : "40x40" }, { "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" + "scale" : "2x", + "size" : "60x60" }, { "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" + "scale" : "3x", + "size" : "60x60" }, { "idiom" : "ipad", - "size" : "20x20", - "scale" : "1x" + "scale" : "1x", + "size" : "20x20" }, { "idiom" : "ipad", - "size" : "20x20", - "scale" : "2x" + "scale" : "2x", + "size" : "20x20" }, { "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" + "scale" : "1x", + "size" : "29x29" }, { "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" + "scale" : "2x", + "size" : "29x29" }, { "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" + "scale" : "1x", + "size" : "40x40" }, { "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" + "scale" : "2x", + "size" : "40x40" }, { "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" + "scale" : "1x", + "size" : "76x76" }, { "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" + "scale" : "2x", + "size" : "76x76" }, { "idiom" : "ipad", - "size" : "83.5x83.5", - "scale" : "2x" + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } } diff --git a/apps/HelloiOS/HelloiOS/Images.xcassets/LaunchImage.launchimage/Contents.json b/apps/HelloiOS/HelloiOS/Images.xcassets/LaunchImage.launchimage/Contents.json deleted file mode 100644 index 4458b40c0558..000000000000 --- a/apps/HelloiOS/HelloiOS/Images.xcassets/LaunchImage.launchimage/Contents.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "images" : [ - { - "orientation" : "portrait", - "idiom" : "iphone", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "2x" - }, - { - "orientation" : "portrait", - "idiom" : "iphone", - "subtype" : "retina4", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "2x" - }, - { - "orientation" : "portrait", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "1x" - }, - { - "orientation" : "landscape", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "1x" - }, - { - "orientation" : "portrait", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "2x" - }, - { - "orientation" : "landscape", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/apps/HelloiOS/HelloiOS/SceneDelegate.h b/apps/HelloiOS/HelloiOS/SceneDelegate.h new file mode 100644 index 000000000000..387791eaa4ae --- /dev/null +++ b/apps/HelloiOS/HelloiOS/SceneDelegate.h @@ -0,0 +1,7 @@ +#import + +@interface SceneDelegate : UIResponder + +@property(strong, nonatomic) UIWindow *window; + +@end diff --git a/apps/HelloiOS/HelloiOS/SceneDelegate.mm b/apps/HelloiOS/HelloiOS/SceneDelegate.mm new file mode 100644 index 000000000000..f64d63ec8888 --- /dev/null +++ b/apps/HelloiOS/HelloiOS/SceneDelegate.mm @@ -0,0 +1,32 @@ +#import "SceneDelegate.h" +#import "HalideViewController.h" + +@implementation SceneDelegate + +- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { + if (![scene isKindOfClass:[UIWindowScene class]]) { + return; + } + + UIWindowScene *windowScene = (UIWindowScene *)scene; + self.window = [[UIWindow alloc] initWithWindowScene:windowScene]; + self.window.rootViewController = [[HalideViewController alloc] init]; + [self.window makeKeyAndVisible]; +} + +- (void)sceneDidDisconnect:(UIScene *)scene { +} + +- (void)sceneDidBecomeActive:(UIScene *)scene { +} + +- (void)sceneWillResignActive:(UIScene *)scene { +} + +- (void)sceneWillEnterForeground:(UIScene *)scene { +} + +- (void)sceneDidEnterBackground:(UIScene *)scene { +} + +@end diff --git a/apps/HelloiOS/README.md b/apps/HelloiOS/README.md new file mode 100644 index 000000000000..2d221b7d0915 --- /dev/null +++ b/apps/HelloiOS/README.md @@ -0,0 +1,91 @@ +# HelloiOS + +An interactive reaction-diffusion iOS demo powered by Halide. + +## Quick Start + +```bash +cd apps/HelloiOS +Halide_ROOT=/path/to/halide/install ./setup.sh +open HelloiOS.xcworkspace +``` + +Then select the **HelloiOS** scheme in Xcode, pick a destination, and build. + +## How It Works + +The project is split into two parts: + +1. **Generators/** -- A CMake project that builds the Halide generator on the + host and runs it to produce iOS-targeted static libraries and headers for + each reaction-diffusion kernel. Kernels are generated for both `iphoneos` and + `iphonesimulator` platforms, so you can switch destinations in Xcode without + re-running setup. + +2. **HelloiOS/** -- A native Xcode project for the iOS app. It compiles the + Objective-C++ sources and links the generated Halide libraries. The app + renders a reaction-diffusion simulation using Metal, with a CPU alternative + available via double-tap. + +Both live in **HelloiOS.xcworkspace**. A one-time `setup.sh` configures the +CMake side and bootstraps `HalideKernels.xcframework`, which bundles the +generated kernels, runtimes, and headers for both device and simulator. The +Xcode project links the xcframework directly. A Run Script build phase rebuilds +it so generator changes are picked up automatically. + +## Prerequisites + +Halide must be built (or installed) first: + +```bash +cd /path/to/Halide +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build +cmake --install build --prefix install +``` + +## Environment Variables + +| Variable | Default | Description | +| ------------- | ------------ | --------------------- | +| `Halide_ROOT` | *(required)* | Halide install prefix | + +## Launching in Simulator + +```bash +open -a Simulator +xcrun simctl bootstatus "iPhone 16" -b +xcrun simctl install booted HelloiOS/build/Release-iphonesimulator/HelloiOS.app +xcrun simctl launch booted org.halide.HelloiOS +``` + +Or use Xcode's Run button after building for a simulator destination. + +## Project Structure + +``` +HelloiOS/ + HelloiOS.xcworkspace/ Top-level workspace + Generators/ + CMakeLists.txt Builds generator + packages HalideKernels.xcframework + reaction_diffusion_2_generator.cpp + build/ Created by setup.sh + HelloiOS/ + HelloiOS.xcodeproj/ Native Xcode project for the iOS app + *.h / *.mm App source files + setup.sh One-time setup script +``` + +## Troubleshooting + +Old Xcode versions may incorrectly think the generator binaries are being built +for iOS, and may want signing of the generator binary itself. If this happens, +the nuclear option is to prefix the xcodebuild invocation in the 'Build Halide +Generators' build phase of the HelloiOS target with: + +``` +env -i USER="$USER" HOME="$HOME" PATH="$PATH" DEVELOPER_DIR="$DEVELOPER_DIR" +``` + +Only do this if necessary as it drops *all* build context and may cause +different problems in the future. diff --git a/apps/HelloiOS/setup.sh b/apps/HelloiOS/setup.sh new file mode 100755 index 000000000000..b5a882a7c59f --- /dev/null +++ b/apps/HelloiOS/setup.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -euo pipefail + +: "${Halide_ROOT:?Set Halide_ROOT in the environment to the Halide install prefix.}" + +cd "$(dirname "${BASH_SOURCE[0]}")" + +cmake -G Xcode -S Generators -B Generators/build + +# Bootstrap: build kernels and package the xcframework so Xcode can load +# the project. Subsequent builds are driven by the Xcode run-script phase. +cmake --build Generators/build --config Release + +echo "" +echo "Setup complete. Open HelloiOS.xcworkspace in Xcode to build and run." +echo "" +echo " open $(pwd)/HelloiOS.xcworkspace" +echo "" +echo "To run on a real device, you will need to set the development team " +echo "in the Xcode project settings. Check 'Automatically manage signing'" +echo "under the Signing & Capabilities tab of the HelloiOS project settings." +echo "" +echo "If you encounter build problems, check the troubleshooting section of" +echo "the README. If that section doesn't help and you solve your build" +echo "problems regardless, we would welcome a PR that updates the" +echo "troubleshooting section of the README." +echo +echo "Known issue: You must build-and-run in Xcode twice to see the effect" +echo "of changes to the generator source (issue #9049)." diff --git a/cmake/HalideGeneratorHelpers.cmake b/cmake/HalideGeneratorHelpers.cmake index 0a17ab15c8dc..ef7e32d87b47 100644 --- a/cmake/HalideGeneratorHelpers.cmake +++ b/cmake/HalideGeneratorHelpers.cmake @@ -66,6 +66,7 @@ function(add_halide_generator TARGET) if (NOT TARGET "${gen}") if (NOT TARGET "${ARG_PACKAGE_NAME}") add_custom_target("${ARG_PACKAGE_NAME}") + set_property(TARGET "${ARG_PACKAGE_NAME}" PROPERTY FOLDER "Halide Internal") endif () if (ARG_SOURCES MATCHES ".py$") @@ -109,6 +110,18 @@ function(add_halide_generator TARGET) $<$:-Wdeprecated-declarations> $<$:/w14996> # 4996: compiler encountered deprecated declaration ) + + if (CMAKE_GENERATOR STREQUAL "Xcode") + set_target_properties( + ${TARGET} PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_REQUIRED NO) + add_custom_command( + TARGET ${TARGET} POST_BUILD + COMMAND codesign --sign - $ + COMMENT "Ad-hoc signing ${TARGET}" + ) + endif () endif () add_dependencies("${ARG_PACKAGE_NAME}" ${TARGET}) @@ -168,10 +181,23 @@ endfunction() function(_Halide_library_from_generator TARGET) cmake_parse_arguments( PARSE_ARGV 1 ARG "" - "FUNCTION_NAME;GENERATOR;GRADIENT_DESCENT;TYPE;USE_RUNTIME" + "FILE_BASE_NAME;FUNCTION_NAME;GENERATOR;GRADIENT_DESCENT;OUTPUT_DIR;TYPE;USE_RUNTIME" "COMMAND;DEPENDS;EXTRA_OUTPUTS;PARAMS;PLUGINS;TARGETS" ) + if (NOT ARG_FILE_BASE_NAME) + set(ARG_FILE_BASE_NAME "${TARGET}") + endif () + + # OUTPUT_DIR is an absolute path. Compute a relative prefix for + # add_custom_command OUTPUT paths to avoid Xcode .rule file collisions. + cmake_path(SET ARG_OUTPUT_DIR "${ARG_OUTPUT_DIR}") + cmake_path(ABSOLUTE_PATH ARG_OUTPUT_DIR BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" NORMALIZE) + cmake_path(RELATIVE_PATH ARG_OUTPUT_DIR BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" OUTPUT_VARIABLE output_prefix) + if (output_prefix) + string(APPEND output_prefix "/") + endif () + ## "hash table" of extra outputs to extensions # Keep in sync with Module.cpp # keep-sorted start ignore_prefixes=# @@ -236,24 +262,24 @@ function(_Halide_library_from_generator TARGET) ## Check the type to determine outputs set(outputs c_header) - set(output_files "${TARGET}.h") + set(output_files "${output_prefix}${ARG_FILE_BASE_NAME}.h") list(LENGTH ARG_TARGETS num_targets) if (ARG_TYPE STREQUAL "object" AND num_targets GREATER 1) foreach (t IN LISTS ARG_TARGETS) - _Halide_add_output("${ARG_TYPE}" "${TARGET}-${t}") + _Halide_add_output("${ARG_TYPE}" "${output_prefix}${ARG_FILE_BASE_NAME}-${t}") endforeach () - _Halide_add_output("${ARG_TYPE}" "${TARGET}_wrapper") + _Halide_add_output("${ARG_TYPE}" "${output_prefix}${ARG_FILE_BASE_NAME}_wrapper") else () # c_source, static_library, or object with one target - _Halide_add_output("${ARG_TYPE}" "${TARGET}") + _Halide_add_output("${ARG_TYPE}" "${output_prefix}${ARG_FILE_BASE_NAME}") endif () foreach (output IN LISTS ARG_EXTRA_OUTPUTS) if (output STREQUAL "c_source" AND ARG_TYPE STREQUAL "c_source") # Skip this as it was handled above else () - _Halide_add_output("${output}" "${TARGET}") + _Halide_add_output("${output}" "${output_prefix}${ARG_FILE_BASE_NAME}") endif () endforeach () @@ -261,13 +287,13 @@ function(_Halide_library_from_generator TARGET) add_custom_command( OUTPUT ${output_files} COMMAND ${ARG_COMMAND} - -n "${TARGET}" + -n "${ARG_FILE_BASE_NAME}" -d "$" -g "${ARG_GENERATOR}" -f "${ARG_FUNCTION_NAME}" -e "$,$>>" ${plugins_args} - -o . + -o "${ARG_OUTPUT_DIR}" "target=$>" ${ARG_PARAMS} DEPENDS ${ARG_DEPENDS} ${ARG_PLUGINS} @@ -312,6 +338,7 @@ function(_Halide_library_from_generator TARGET) endif () add_custom_target("${TARGET}.update" DEPENDS ${output_files}) + set_property(TARGET "${TARGET}.update" PROPERTY FOLDER "Halide Internal") add_dependencies("${TARGET}" "${TARGET}.update") target_link_libraries("${TARGET}" INTERFACE "${ARG_USE_RUNTIME}") @@ -323,7 +350,7 @@ function(_Halide_library_from_generator TARGET) target_sources("${TARGET}" INTERFACE FILE_SET HEADERS - BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}" + BASE_DIRS "${ARG_OUTPUT_DIR}" FILES "${local_out_c_header}") endfunction() @@ -489,7 +516,7 @@ function(add_halide_library TARGET) endforeach () set(options C_BACKEND GRADIENT_DESCENT) - set(oneValueArgs FROM GENERATOR FUNCTION_NAME NAMESPACE USE_RUNTIME AUTOSCHEDULER HEADER ${extra_output_names} NO_THREADS NO_DL_LIBS) + set(oneValueArgs FILE_BASE_NAME FROM GENERATOR FUNCTION_NAME NAMESPACE OUTPUT_DIR USE_RUNTIME AUTOSCHEDULER HEADER ${extra_output_names} NO_THREADS NO_DL_LIBS) set(multiValueArgs DEPENDS TARGETS PARAMS PLUGINS ${features_args}) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -509,6 +536,10 @@ function(add_halide_library TARGET) message(AUTHOR_WARNING "The C backend sources will be compiled with the current CMake toolchain.") endif () + if (NOT ARG_FILE_BASE_NAME) + set(ARG_FILE_BASE_NAME "${TARGET}") + endif () + if (NOT ARG_GENERATOR) set(ARG_GENERATOR "${TARGET}") endif () @@ -517,6 +548,9 @@ function(add_halide_library TARGET) set(ARG_FUNCTION_NAME "${TARGET}") endif () + cmake_path(SET ARG_OUTPUT_DIR "${ARG_OUTPUT_DIR}") + cmake_path(ABSOLUTE_PATH ARG_OUTPUT_DIR BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" NORMALIZE) + if (ARG_NAMESPACE) set(ARG_FUNCTION_NAME "${ARG_NAMESPACE}::${ARG_FUNCTION_NAME}") endif () @@ -572,6 +606,8 @@ function(add_halide_library TARGET) add_halide_runtime( "${TARGET}.runtime" + FILE_BASE_NAME "${ARG_FILE_BASE_NAME}.runtime" + OUTPUT_DIR "${ARG_OUTPUT_DIR}" NO_DEFAULT_TARGETS TARGETS ${ARG_TARGETS} ${runtime_args} ) @@ -638,9 +674,11 @@ function(add_halide_library TARGET) COMMAND ${generator_cmd} DEPENDS ${generator_cmd_deps} ${ARG_DEPENDS} EXTRA_OUTPUTS ${extra_outputs} + FILE_BASE_NAME "${ARG_FILE_BASE_NAME}" FUNCTION_NAME "${ARG_FUNCTION_NAME}" GENERATOR "${ARG_GENERATOR}" GRADIENT_DESCENT "${ARG_GRADIENT_DESCENT}" + OUTPUT_DIR "${ARG_OUTPUT_DIR}" PARAMS ${ARG_PARAMS} PLUGINS ${ARG_PLUGINS} TYPE "${library_type}" @@ -703,9 +741,13 @@ function(add_halide_library TARGET) "${TARGET}" ${generator_args} TARGETS ${ARG_TARGETS}) endif () - # Save some info for add_halide_python_extension_library() in case it is used for this target. + # Save metadata on the library target for downstream helpers. + set_property(DIRECTORY APPEND PROPERTY Halide_BUILDSYSTEM_TARGETS "${TARGET}") # nolint set_property(TARGET "${TARGET}" PROPERTY Halide_LIBRARY_RUNTIME_TARGET "${ARG_USE_RUNTIME}") set_property(TARGET "${TARGET}" PROPERTY Halide_LIBRARY_FUNCTION_NAME "${ARG_FUNCTION_NAME}") + set_property(TARGET "${TARGET}" PROPERTY Halide_LIBRARY_TARGETS "${ARG_TARGETS}") + set_property(TARGET "${TARGET}" PROPERTY Halide_LIBRARY_FILE_BASE_NAME "${ARG_FILE_BASE_NAME}") + set_property(TARGET "${TARGET}" PROPERTY Halide_LIBRARY_OUTPUT_DIR "${ARG_OUTPUT_DIR}") if ("python_extension" IN_LIST extra_outputs) list(GET OUT_python_extension 0 py_ext_cpp) # These files should always be identical set_property(TARGET "${TARGET}" PROPERTY Halide_LIBRARY_PYTHON_EXTENSION_CPP "${py_ext_cpp}") @@ -878,10 +920,23 @@ endfunction() function(add_halide_runtime RT) set(options NO_DEFAULT_TARGETS) - set(oneValueArgs NO_THREADS NO_DL_LIBS) + set(oneValueArgs FILE_BASE_NAME NO_THREADS NO_DL_LIBS OUTPUT_DIR) set(multiValueArgs TARGETS) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if (NOT ARG_FILE_BASE_NAME) + set(ARG_FILE_BASE_NAME "${RT}") + endif () + + # OUTPUT_DIR is an absolute path. Compute a relative prefix for + # add_custom_command OUTPUT paths to avoid Xcode .rule file collisions. + cmake_path(SET ARG_OUTPUT_DIR "${ARG_OUTPUT_DIR}") + cmake_path(ABSOLUTE_PATH ARG_OUTPUT_DIR BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" NORMALIZE) + cmake_path(RELATIVE_PATH ARG_OUTPUT_DIR BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" OUTPUT_VARIABLE output_prefix) + if (output_prefix) + string(APPEND output_prefix "/") + endif () + # If no TARGETS argument, use Halide_TARGET instead if (NOT ARG_TARGETS) set(ARG_TARGETS "${Halide_TARGET}") @@ -913,20 +968,21 @@ function(add_halide_runtime RT) set(target_list "$") if (is_crosscompiling) - set(GEN_OUTS "${RT}${static_library_extension}") + set(GEN_OUTS "${output_prefix}${ARG_FILE_BASE_NAME}${static_library_extension}") add_custom_command( OUTPUT "${GEN_OUTS}" - COMMAND Halide::GenRT -r "${RT}" -o . + COMMAND Halide::GenRT -r "${ARG_FILE_BASE_NAME}" -o "${ARG_OUTPUT_DIR}" "target=$,$>" DEPENDS Halide::GenRT VERBATIM) add_custom_target("${RT}.update" DEPENDS "${GEN_OUTS}") + set_property(TARGET "${RT}.update" PROPERTY FOLDER "Halide Internal") add_library("${RT}" STATIC IMPORTED GLOBAL) add_dependencies("${RT}" "${RT}.update") set_target_properties("${RT}" PROPERTIES - IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/${GEN_OUTS}) + IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/${GEN_OUTS}") else () list(LENGTH Halide_CMAKE_TARGET num_platforms) if (common_triple STREQUAL "cmake" AND num_platforms GREATER 1) @@ -945,13 +1001,13 @@ function(add_halide_runtime RT) endif () add_custom_command( - OUTPUT "${this_rt}${object_extension}" - COMMAND Halide::GenRT -r "${this_rt}" -o . -e object + OUTPUT "${output_prefix}${this_rt}${object_extension}" + COMMAND Halide::GenRT -r "${this_rt}" -o "${ARG_OUTPUT_DIR}" -e object "target=$,$>" DEPENDS Halide::GenRT VERBATIM) - add_library("${this_rt}" STATIC "${this_rt}${object_extension}") + add_library("${this_rt}" STATIC "${ARG_OUTPUT_DIR}/${this_rt}${object_extension}") set_target_properties("${this_rt}" PROPERTIES LINKER_LANGUAGE CXX) _Halide_fix_xcode("${this_rt}") _Halide_set_osx_arch("${this_rt}" "${triple}") @@ -961,12 +1017,12 @@ function(add_halide_runtime RT) else () set(target_list "$") add_custom_command( - OUTPUT "${RT}${object_extension}" - COMMAND Halide::GenRT -r "${RT}" -o . -e object + OUTPUT "${output_prefix}${ARG_FILE_BASE_NAME}${object_extension}" + COMMAND Halide::GenRT -r "${ARG_FILE_BASE_NAME}" -o "${ARG_OUTPUT_DIR}" -e object "target=$,$>" DEPENDS Halide::GenRT VERBATIM) - add_library("${RT}" STATIC "${RT}${object_extension}") + add_library("${RT}" STATIC "${ARG_OUTPUT_DIR}/${ARG_FILE_BASE_NAME}${object_extension}") set_target_properties("${RT}" PROPERTIES LINKER_LANGUAGE CXX) _Halide_fix_xcode("${RT}") endif () @@ -1080,3 +1136,127 @@ function(_Halide_target_export_single_symbol TARGET SYMBOL) GNU_LD "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.${SYMBOL}.ldscript" ) endfunction() + +## +# Create an XCFramework bundling Halide-generated libraries for multiple Apple platforms. +## + +function(_Halide_targets_to_apple_platform OUT_PLATFORM) + # Parse the arch-bits-os triple and check for "simulator" among features. + # Input: one or more Halide target strings (e.g. "arm-64-ios-simulator-metal-user_context"). + list(GET ARGN 0 first_target) + if (NOT first_target MATCHES "^[^-]+-[^-]+-([^-]+)") + message(FATAL_ERROR "Cannot parse Halide target: ${first_target}") + endif () + + set(os "${CMAKE_MATCH_1}") + if (NOT os STREQUAL "ios") + message(FATAL_ERROR "Unsupported Apple OS '${os}' in target '${first_target}'. " + "add_halide_xcframework currently supports iOS targets only.") + endif () + + if ("${ARGN}" MATCHES "simulator") + set(${OUT_PLATFORM} "iphonesimulator" PARENT_SCOPE) + else () + set(${OUT_PLATFORM} "iphoneos" PARENT_SCOPE) + endif () +endfunction() + +function(add_halide_xcframework TARGET) + set(options "") + set(oneValueArgs "") + set(multiValueArgs LIBRARIES) + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT ARG_LIBRARIES) + message(FATAL_ERROR "add_halide_xcframework requires LIBRARIES") + endif () + + if (ARG_LIBRARIES STREQUAL "ALL") + get_directory_property(ARG_LIBRARIES Halide_BUILDSYSTEM_TARGETS) + if (NOT ARG_LIBRARIES) + message(FATAL_ERROR "add_halide_xcframework: LIBRARIES ALL but no Halide targets found in this directory") + endif () + endif () + + # Collect per-platform information from library target properties. + set(all_platforms "") + foreach (lib IN LISTS ARG_LIBRARIES) + get_property(targets TARGET "${lib}" PROPERTY Halide_LIBRARY_TARGETS) + get_property(base_name TARGET "${lib}" PROPERTY Halide_LIBRARY_FILE_BASE_NAME) + get_property(output_dir TARGET "${lib}" PROPERTY Halide_LIBRARY_OUTPUT_DIR) + + if (NOT targets) + message(FATAL_ERROR "Target '${lib}' is missing Halide_LIBRARY_TARGETS. " + "Was it created with add_halide_library?") + endif () + + _Halide_targets_to_apple_platform(platform ${targets}) + + if (NOT platform IN_LIST all_platforms) + list(APPEND all_platforms "${platform}") + endif () + + # Accumulate per-platform data in list variables. + list(APPEND libs_${platform} "${output_dir}/${base_name}.a") + list(APPEND libs_${platform} "${output_dir}/${base_name}.runtime.a") + list(APPEND hdrs_${platform} "${output_dir}/${base_name}.h") + endforeach () + + # Locate Halide runtime headers. + set(halide_inc "$") + + set(staging "${CMAKE_CURRENT_BINARY_DIR}/_xcfw_staging") + set(xcfw "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.xcframework") + + set(xcfw_commands + COMMAND ${CMAKE_COMMAND} -E rm -rf "${staging}" "${xcfw}") + set(xcfw_create_args "") + + foreach (platform IN LISTS all_platforms) + list(APPEND xcfw_commands + COMMAND ${CMAKE_COMMAND} -E make_directory "${staging}/${platform}/Headers") + + # Copy generated kernel headers. + foreach (hdr IN LISTS hdrs_${platform}) + cmake_path(GET hdr FILENAME hdr_name) + list(APPEND xcfw_commands + COMMAND ${CMAKE_COMMAND} -E copy + "${hdr}" "${staging}/${platform}/Headers/${hdr_name}") + endforeach () + + # Copy Halide runtime headers. + foreach (hdr_name IN ITEMS + HalideBuffer.h + HalideRuntime.h + HalideRuntimeCuda.h + HalideRuntimeD3D12Compute.h + HalideRuntimeHexagonDma.h + HalideRuntimeHexagonHost.h + HalideRuntimeMetal.h + HalideRuntimeOpenCL.h + HalideRuntimeQurt.h + HalideRuntimeVulkan.h + HalideRuntimeWebGPU.h) + list(APPEND xcfw_commands + COMMAND ${CMAKE_COMMAND} -E copy "${halide_inc}/${hdr_name}" "${staging}/${platform}/Headers/${hdr_name}") + endforeach () + + # Merge all static libraries for this platform into one. + list(APPEND xcfw_commands + COMMAND libtool -static -o "${staging}/${platform}/lib${TARGET}.a" ${libs_${platform}}) + + list(APPEND xcfw_create_args + -library "${staging}/${platform}/lib${TARGET}.a" + -headers "${staging}/${platform}/Headers") + endforeach () + + add_custom_target( + ${TARGET} ALL + DEPENDS ${ARG_LIBRARIES} + ${xcfw_commands} + COMMAND xcodebuild -create-xcframework ${xcfw_create_args} -output "${xcfw}" + COMMENT "Packaging ${TARGET}.xcframework" + ) + set_property(TARGET ${TARGET} PROPERTY FOLDER "Halide Internal") +endfunction()