Remove per-call overhead from API access and text conversion

Two hot-path optimizations for the device's Cortex-M7 (and debug simulator
builds):

- The ten sub-API pointers are cached once in Playdate.initialize(with:);
  wrapper accessors now return UnsafePointer and call sites read a single
  field through it, instead of unwrapping the optional API struct and
  copying a whole sub-API struct of function pointers on every call.
  Nested sub-APIs (sound classes, effects, video, tilemap, http/tcp)
  derive from the cached pointers with one field load.

- String -> C conversions (withPlaydateCString, and a new withPlaydateUTF8
  used by the text drawing/measuring APIs) use withUnsafeTemporaryAllocation
  instead of building a ContiguousArray, so logging and drawText in the
  update loop no longer heap-allocate per call. Verified within the
  Embedded Swift subset by the device cross-compile.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-25 07:23:19 +02:00
co-authored by Claude Fable 5
parent e4e99cc894
commit d914e0f8fb
20 changed files with 682 additions and 643 deletions
+4 -4
View File
@@ -9,7 +9,7 @@
internal import CPlaydate
private var jsonAPI: playdate_json { Playdate.api.json.pointee }
private var jsonAPI: UnsafePointer<playdate_json> { Playdate.jsonAPI }
/// The JSON API: decoding to and encoding from a `Value` tree.
public enum JSON {}
@@ -112,7 +112,7 @@ extension JSON {
var outval = json_value()
let ok = jsonString.withPlaydateCString { cString in
withExtendedLifetime(context) {
jsonAPI.decodeString.unsafelyUnwrapped(&decoder, cString, &outval) != 0
jsonAPI.pointee.decodeString.unsafelyUnwrapped(&decoder, cString, &outval) != 0
}
}
guard ok else {
@@ -141,7 +141,7 @@ extension JSON {
var outval = json_value()
let ok = withExtendedLifetime(context) {
withExtendedLifetime(file) {
jsonAPI.decode.unsafelyUnwrapped(&decoder, reader, &outval) != 0
jsonAPI.pointee.decode.unsafelyUnwrapped(&decoder, reader, &outval) != 0
}
}
guard ok else {
@@ -172,7 +172,7 @@ extension JSON {
private let output = Output()
public init(pretty: Bool = false) {
jsonAPI.initEncoder.unsafelyUnwrapped(&encoder, { userdata, string, length in
jsonAPI.pointee.initEncoder.unsafelyUnwrapped(&encoder, { userdata, string, length in
guard let userdata, let string else { return }
let output = Unmanaged<Output>.fromOpaque(userdata).takeUnretainedValue()
let bytes = UnsafeRawBufferPointer(start: string, count: Int(length))