From aa220ad14219edf1a2e4f50b83131bb1ca715379 Mon Sep 17 00:00:00 2001 From: pcbeard Date: Fri, 29 Mar 2024 13:11:41 -0700 Subject: [PATCH 1/5] Try building static libraries --- Examples/SwiftBreak/Package.swift | 18 +++++++++++------- Examples/SwiftBreak/buildit | 9 +++++++++ Package.swift | 9 ++++++--- 3 files changed, 26 insertions(+), 10 deletions(-) create mode 100755 Examples/SwiftBreak/buildit diff --git a/Examples/SwiftBreak/Package.swift b/Examples/SwiftBreak/Package.swift index 3c81195..3531696 100644 --- a/Examples/SwiftBreak/Package.swift +++ b/Examples/SwiftBreak/Package.swift @@ -12,10 +12,11 @@ let swiftSettingsSimulator: [SwiftSetting] = [ .enableExperimentalFeature("Embedded"), .enableExperimentalFeature("NoncopyableGenerics"), .unsafeFlags([ + "-g", "-Onone", "-Xfrontend", "-disable-objc-interop", "-Xfrontend", "-disable-stack-protector", "-Xfrontend", "-function-sections", - "-Xfrontend", "-gline-tables-only", + // "-Xfrontend", "-gline-tables-only", "-Xcc", "-DTARGET_EXTENSION", "-Xcc", "-I", "-Xcc", "\(gccIncludePrefix)/include", "-Xcc", "-I", "-Xcc", "\(gccIncludePrefix)/include-fixed", @@ -27,17 +28,20 @@ let swiftSettingsSimulator: [SwiftSetting] = [ let package = Package( name: "SwiftBreak", products: [ - .library(name: "SwiftBreak", targets: ["SwiftBreak"]) + .library(name: "SwiftBreak", type: .static, targets: ["SwiftBreak"]), ], dependencies: [ .package(path: "../..") ], targets: [ .target( - name: "SwiftBreak", - dependencies: [ - .product(name: "Playdate", package: "swift-playdate-examples") - ], - swiftSettings: swiftSettingsSimulator) + name: "SwiftBreak", + dependencies: [ + .product(name: "Playdate", package: "swift-playdate-examples"), + .product(name: "CPlaydate", package: "swift-playdate-examples"), + ], + path: "Sources", + swiftSettings: swiftSettingsSimulator + ) ], swiftLanguageVersions: [.version("6"), .v5]) diff --git a/Examples/SwiftBreak/buildit b/Examples/SwiftBreak/buildit new file mode 100755 index 0000000..630c843 --- /dev/null +++ b/Examples/SwiftBreak/buildit @@ -0,0 +1,9 @@ +#!/bin/sh +# swift build -Xcc -c -Xswiftc -g -Xswiftc -Onone -c release --verbose +swift build -c release --verbose +clang -g -nostdlib -Wl,-exported_symbol,_eventHandlerShim -Wl,-exported_symbol,_eventHandler \ +-dynamiclib -rdynamic -lm \ +.build/release/libPlaydate.a .build/release/libSwiftBreak.a .build/release/libCPlaydate.a \ +-o build/pdex.dylib +cp build/pdex.dylib Source +${HOME}/Developer/PlaydateSDK/bin/pdc Source SwiftBreak.pdx diff --git a/Package.swift b/Package.swift index 18c2e19..757cff4 100644 --- a/Package.swift +++ b/Package.swift @@ -11,10 +11,12 @@ guard let home = Context.environment["HOME"] else { let swiftSettingsSimulator: [SwiftSetting] = [ .enableExperimentalFeature("Embedded"), .unsafeFlags([ + "-g", "-Onone", + "-Xfrontend", "-Onone", "-Xfrontend", "-disable-objc-interop", "-Xfrontend", "-disable-stack-protector", "-Xfrontend", "-function-sections", - "-Xfrontend", "-gline-tables-only", + // "-Xfrontend", "-gline-tables-only", "-Xcc", "-DTARGET_EXTENSION", "-Xcc", "-I", "-Xcc", "\(gccIncludePrefix)/include", "-Xcc", "-I", "-Xcc", "\(gccIncludePrefix)/include-fixed", @@ -26,6 +28,7 @@ let swiftSettingsSimulator: [SwiftSetting] = [ let cSettingsSimulator: [CSetting] = [ .unsafeFlags([ "-DTARGET_EXTENSION", + "-nostdlib", "-I", "\(gccIncludePrefix)/include", "-I", "\(gccIncludePrefix)/include-fixed", "-I", "\(gccIncludePrefix)/../../../../arm-none-eabi/include", @@ -36,8 +39,8 @@ let cSettingsSimulator: [CSetting] = [ let package = Package( name: "swift-playdate-examples", products: [ - .library(name: "Playdate", targets: ["Playdate"]), - .library(name: "CPlaydate", targets: ["CPlaydate"]), + .library(name: "Playdate", type: .static, targets: ["Playdate"]), + .library(name: "CPlaydate", type: .static, targets: ["CPlaydate"]), ], targets: [ .target( From 3da58b65cf165803dc219838e7d94dce56e93a58 Mon Sep 17 00:00:00 2001 From: pcbeard Date: Fri, 29 Mar 2024 14:33:51 -0700 Subject: [PATCH 2/5] Fix exclusive access bug Can't call Sprite.updateAndDrawSprites() from within .updateGame() because Sprite.ball() installs a callback that accesses game.state. Discovered while debugging the simulator code using non-embedded Swift. --- Examples/SwiftBreak/Sources/Entry.swift | 4 +++- Examples/SwiftBreak/Sources/Game.swift | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Examples/SwiftBreak/Sources/Entry.swift b/Examples/SwiftBreak/Sources/Entry.swift index b1117b8..91dfe6f 100644 --- a/Examples/SwiftBreak/Sources/Entry.swift +++ b/Examples/SwiftBreak/Sources/Entry.swift @@ -10,7 +10,9 @@ nonisolated(unsafe) var game = Game() @_cdecl("update") func update(pointer: UnsafeMutableRawPointer?) -> Int32 { - game.updateGame() + if game.updateGame() { + Sprite.updateAndDrawSprites() + } return 1 } diff --git a/Examples/SwiftBreak/Sources/Game.swift b/Examples/SwiftBreak/Sources/Game.swift index 9b936ba..9b24e10 100644 --- a/Examples/SwiftBreak/Sources/Game.swift +++ b/Examples/SwiftBreak/Sources/Game.swift @@ -53,9 +53,10 @@ struct Game: ~Copyable { } extension Game { - mutating func updateGame() { + mutating func updateGame() -> Bool { let pushed = System.buttonState.pushed - switch self.state { + let state = self.state + switch state { case .loading: if pushed == .a { self.startNewGame() @@ -89,8 +90,8 @@ extension Game { self.startNewGame() } } - - Sprite.updateAndDrawSprites() + + return true } mutating func startNewGame() { From 8d882f9f696df9d1f6c6b7ec11c1ab749420aee1 Mon Sep 17 00:00:00 2001 From: pcbeard Date: Fri, 29 Mar 2024 14:56:06 -0700 Subject: [PATCH 3/5] Use System.free() instead of .deallocate() This ensures that the memory is always freed by the Playdate System memory allocator. --- Sources/Playdate/Sprite.swift | 18 +++++++++++------- Sources/Playdate/System.swift | 12 ++++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Sources/Playdate/Sprite.swift b/Sources/Playdate/Sprite.swift index 2d79cc6..354d066 100644 --- a/Sources/Playdate/Sprite.swift +++ b/Sources/Playdate/Sprite.swift @@ -361,7 +361,11 @@ extension Sprite { let rawCollisions = spriteAPI.checkCollisions.unsafelyUnwrapped(self.pointer, goalX, goalY, &actualX, &actualY, &count) let collisions = UnsafeBufferPointer(start: rawCollisions, count: Int(count)) - defer { rawCollisions?.deallocate() } + defer { + if let rawCollisions { + System.free(pointer: rawCollisions) + } + } return body(actualX, actualY, collisions) } @@ -397,7 +401,7 @@ extension Sprite { let rawCollisions = spriteAPI.moveWithCollisions.unsafelyUnwrapped(self.pointer, goalX, goalY, &actualX, &actualY, &count) let collisions = UnsafeBufferPointer(start: rawCollisions, count: Int(count)) - defer { rawCollisions?.deallocate() } + defer { rawCollisions.flatMap(System.free) } return body(actualX, actualY, collisions) } @@ -407,7 +411,7 @@ extension Sprite { var count: Int32 = 0 let rawSprites = spriteAPI.querySpritesAtPoint.unsafelyUnwrapped(x, y, &count) let sprites = UnsafeBufferPointer(start: rawSprites, count: Int(count)) - defer { rawSprites?.deallocate() } + defer { rawSprites.flatMap(System.free) } return body(sprites) } @@ -417,7 +421,7 @@ extension Sprite { var count: Int32 = 0 let rawSprites = spriteAPI.querySpritesInRect.unsafelyUnwrapped(x, y, width, height, &count) let sprites = UnsafeBufferPointer(start: rawSprites, count: Int(count)) - defer { rawSprites?.deallocate() } + defer { rawSprites.flatMap(System.free) } return body(sprites) } @@ -427,7 +431,7 @@ extension Sprite { var count: Int32 = 0 let rawSprites = spriteAPI.querySpritesAlongLine.unsafelyUnwrapped(x1, y1, x2, y2, &count) let sprites = UnsafeBufferPointer(start: rawSprites, count: Int(count)) - defer { rawSprites?.deallocate() } + defer { rawSprites.flatMap(System.free) } return body(sprites) } @@ -439,7 +443,7 @@ extension Sprite { var count: Int32 = 0 let rawSprites = spriteAPI.querySpriteInfoAlongLine.unsafelyUnwrapped(x1, y1, x2, y2, &count) let sprites = UnsafeBufferPointer(start: rawSprites, count: Int(count)) - defer { rawSprites?.deallocate() } + defer { rawSprites.flatMap(System.free) } return body(sprites) } @@ -452,7 +456,7 @@ extension Sprite { var count: Int32 = 0 let rawSprites = spriteAPI.allOverlappingSprites.unsafelyUnwrapped(&count) let sprites = UnsafeBufferPointer(start: rawSprites, count: Int(count)) - defer { rawSprites?.deallocate() } + defer { rawSprites.flatMap(System.free) } return body(sprites) } } diff --git a/Sources/Playdate/System.swift b/Sources/Playdate/System.swift index b111d9e..73d6a87 100644 --- a/Sources/Playdate/System.swift +++ b/Sources/Playdate/System.swift @@ -151,4 +151,16 @@ extension System { public static func clearICache() { systemAPI.clearICache.unsafelyUnwrapped() } + + /// Reallocates a pointer. + /// If `pointer == nil`, and `size > 0`, allocates a new pointer. + /// If `pointer != nil` and `size == 0` deallocates the pointer. + public static func realloc(pointer: UnsafeMutableRawPointer?, size: size_t) -> UnsafeMutableRawPointer? { + systemAPI.realloc.unsafelyUnwrapped(pointer, size) + } + + /// Frees a pointer previously allocated with `realloc()`. + public static func free(pointer: UnsafeMutablePointer) { + let _ = systemAPI.realloc.unsafelyUnwrapped(pointer, 0) + } } From f662b49db4920391bfd4fa376af839d4a4df9147 Mon Sep 17 00:00:00 2001 From: pcbeard Date: Fri, 29 Mar 2024 14:57:31 -0700 Subject: [PATCH 4/5] Build for the simulator using SPM. --- Examples/SwiftBreak/buildit | 9 --------- Examples/SwiftBreak/simbuild | 4 ++++ 2 files changed, 4 insertions(+), 9 deletions(-) delete mode 100755 Examples/SwiftBreak/buildit create mode 100755 Examples/SwiftBreak/simbuild diff --git a/Examples/SwiftBreak/buildit b/Examples/SwiftBreak/buildit deleted file mode 100755 index 630c843..0000000 --- a/Examples/SwiftBreak/buildit +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -# swift build -Xcc -c -Xswiftc -g -Xswiftc -Onone -c release --verbose -swift build -c release --verbose -clang -g -nostdlib -Wl,-exported_symbol,_eventHandlerShim -Wl,-exported_symbol,_eventHandler \ --dynamiclib -rdynamic -lm \ -.build/release/libPlaydate.a .build/release/libSwiftBreak.a .build/release/libCPlaydate.a \ --o build/pdex.dylib -cp build/pdex.dylib Source -${HOME}/Developer/PlaydateSDK/bin/pdc Source SwiftBreak.pdx diff --git a/Examples/SwiftBreak/simbuild b/Examples/SwiftBreak/simbuild new file mode 100755 index 0000000..13c61ce --- /dev/null +++ b/Examples/SwiftBreak/simbuild @@ -0,0 +1,4 @@ +#!/bin/sh +swift build +cp .build/arm64-apple-macosx/debug/libSwiftBreak.dylib Source/pdex.dylib +${HOME}/Developer/PlaydateSDK/bin/pdc Source SwiftBreak.pdx From b19fa6da361e5309806b163810bbf763fe03ef3f Mon Sep 17 00:00:00 2001 From: pcbeard Date: Fri, 29 Mar 2024 14:59:10 -0700 Subject: [PATCH 5/5] Don't use embedded Swift when building for the simulator --- Examples/SwiftBreak/Package.swift | 9 +++------ Package.swift | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Examples/SwiftBreak/Package.swift b/Examples/SwiftBreak/Package.swift index 3531696..2b27bd3 100644 --- a/Examples/SwiftBreak/Package.swift +++ b/Examples/SwiftBreak/Package.swift @@ -9,13 +9,11 @@ guard let home = Context.environment["HOME"] else { } let swiftSettingsSimulator: [SwiftSetting] = [ - .enableExperimentalFeature("Embedded"), .enableExperimentalFeature("NoncopyableGenerics"), .unsafeFlags([ - "-g", "-Onone", "-Xfrontend", "-disable-objc-interop", - "-Xfrontend", "-disable-stack-protector", - "-Xfrontend", "-function-sections", + // "-Xfrontend", "-disable-stack-protector", + // "-Xfrontend", "-function-sections", // "-Xfrontend", "-gline-tables-only", "-Xcc", "-DTARGET_EXTENSION", "-Xcc", "-I", "-Xcc", "\(gccIncludePrefix)/include", @@ -28,7 +26,7 @@ let swiftSettingsSimulator: [SwiftSetting] = [ let package = Package( name: "SwiftBreak", products: [ - .library(name: "SwiftBreak", type: .static, targets: ["SwiftBreak"]), + .library(name: "SwiftBreak", type: .dynamic, targets: ["SwiftBreak"]), ], dependencies: [ .package(path: "../..") @@ -38,7 +36,6 @@ let package = Package( name: "SwiftBreak", dependencies: [ .product(name: "Playdate", package: "swift-playdate-examples"), - .product(name: "CPlaydate", package: "swift-playdate-examples"), ], path: "Sources", swiftSettings: swiftSettingsSimulator diff --git a/Package.swift b/Package.swift index 757cff4..bd7ff0f 100644 --- a/Package.swift +++ b/Package.swift @@ -9,7 +9,7 @@ guard let home = Context.environment["HOME"] else { } let swiftSettingsSimulator: [SwiftSetting] = [ - .enableExperimentalFeature("Embedded"), + // .enableExperimentalFeature("Embedded"), .unsafeFlags([ "-g", "-Onone", "-Xfrontend", "-Onone",