serve-sim 0.1.47-beta.127.1 → 0.1.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ runs inside the simulator and exits with the device session.
|
|
|
59
59
|
Duo uses one interactive RealityKit preview of the installed Xcode model, with
|
|
60
60
|
live screen textures and touches mapped to each bent screen half. Apple's model
|
|
61
61
|
stays in Xcode; it is not redistributed. Rotate animates the device and pose
|
|
62
|
-
icons together. Motion uses a faster render target and sharpens when settled.
|
|
62
|
+
icons together. Motion uses a faster 1000×900 render target and sharpens once to 1500×1350 when settled.
|
|
63
63
|
Screenshots capture the active app framebuffer, including the unfolded inner display.
|
|
64
64
|
|
|
65
65
|
Use the pose buttons or hold **Alt** to reveal the continuous hinge slider.
|
|
@@ -8,6 +8,13 @@ import UniformTypeIdentifiers
|
|
|
8
8
|
|
|
9
9
|
/// Renders Apple's installed V68 asset; no Apple asset is copied into the package.
|
|
10
10
|
@MainActor final class DuoRenderer {
|
|
11
|
+
/// Motion target. 10:9 matches the sharpen target, so normalized anchors stay put.
|
|
12
|
+
static let previewWidth = 1000
|
|
13
|
+
static let previewHeight = 900
|
|
14
|
+
/// One-shot settle target for Retina screen text. Never 3000, and MSAA stays off:
|
|
15
|
+
/// 4× MSAA plus a 3000px idle target was the Apple Silicon CPU regression.
|
|
16
|
+
static let sharpenWidth = 1500
|
|
17
|
+
static let sharpenHeight = 1350
|
|
11
18
|
private let renderer: RealityRenderer
|
|
12
19
|
private let wrapper = Entity()
|
|
13
20
|
private let rest = Entity()
|
|
@@ -30,7 +37,7 @@ import UniformTypeIdentifiers
|
|
|
30
37
|
private var screenTextures: [String: TextureResource] = [:]
|
|
31
38
|
private var screenFrames: [String: Data] = [:]
|
|
32
39
|
private var lastAngle: Double = .nan
|
|
33
|
-
//
|
|
40
|
+
// Active target: 1000×900 while moving, 1500×1350 after the one-shot sharpen.
|
|
34
41
|
var width: Int { texture.width }
|
|
35
42
|
var height: Int { texture.height }
|
|
36
43
|
// Use Device Hub's 36 mm sensor model with a longer lens to flatten depth.
|
|
@@ -72,11 +79,13 @@ import UniformTypeIdentifiers
|
|
|
72
79
|
renderer.entities.append(camera)
|
|
73
80
|
renderer.activeCamera = camera
|
|
74
81
|
renderer.lighting.resource = EnvironmentResource.duoObjectLighting()
|
|
75
|
-
|
|
82
|
+
// Preserve screen detail after settle with a second target, not MSAA.
|
|
83
|
+
// Antialiasing the shell at 4× was too expensive for the live stream.
|
|
84
|
+
renderer.cameraSettings.antialiasing = .none
|
|
76
85
|
renderer.cameraSettings.colorBackground = .color(CGColor(red: 0, green: 0, blue: 0, alpha: 0))
|
|
77
86
|
// Keep both targets allocated: resizing during gestures would stall Metal.
|
|
78
|
-
targets = try [
|
|
79
|
-
let descriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm_srgb, width: width, height:
|
|
87
|
+
targets = try [(Self.previewWidth, Self.previewHeight), (Self.sharpenWidth, Self.sharpenHeight)].map { width, height in
|
|
88
|
+
let descriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm_srgb, width: width, height: height, mipmapped: false)
|
|
80
89
|
descriptor.storageMode = .shared
|
|
81
90
|
descriptor.usage = [.renderTarget, .shaderRead]
|
|
82
91
|
guard let texture = device.makeTexture(descriptor: descriptor) else { throw CocoaError(.fileReadUnknown) }
|
|
@@ -86,10 +95,11 @@ import UniformTypeIdentifiers
|
|
|
86
95
|
controller = subject.playAnimation(clip, transitionDuration: 0, startsPaused: true)
|
|
87
96
|
}
|
|
88
97
|
|
|
89
|
-
func render(jpeg: Data, panel: String, angle: Double, roll: Double, fullResolution: Bool =
|
|
98
|
+
func render(jpeg: Data, panel: String, angle: Double, roll: Double, fullResolution: Bool = false) async throws -> Data {
|
|
99
|
+
// False is the fast motion target. True is the one-shot 1500px settle frame.
|
|
90
100
|
targetIndex = fullResolution ? 1 : 0
|
|
91
101
|
guard angle.isFinite, (0...180).contains(angle), roll.isFinite else { throw CocoaError(.fileReadCorruptFile) }
|
|
92
|
-
// Hinge
|
|
102
|
+
// Hinge and rotation updates often reuse the same screen image.
|
|
93
103
|
// Keep both panel textures alive and skip image decoding/upload entirely.
|
|
94
104
|
if screenFrames[panel] != jpeg {
|
|
95
105
|
guard let source = CGImageSourceCreateWithData(jpeg as CFData, nil),
|
|
@@ -36,7 +36,8 @@ private func readRequest() throws -> (Request, Data)? {
|
|
|
36
36
|
guard CommandLine.arguments.count == 2 else { throw CocoaError(.fileReadInvalidFileName) }
|
|
37
37
|
let renderer = try await DuoRenderer(modelURL: URL(fileURLWithPath: CommandLine.arguments[1]))
|
|
38
38
|
while let (request, jpeg) = try await Task.detached(operation: { try readRequest() }).value {
|
|
39
|
-
|
|
39
|
+
// Omitted fullResolution stays on the fast target. True is the 1500px settle sharpen.
|
|
40
|
+
let rendered = try await renderer.render(jpeg: jpeg, panel: request.panel, angle: request.hingeDegrees, roll: request.rollDegrees, fullResolution: request.fullResolution ?? false)
|
|
40
41
|
let header = try JSONSerialization.data(withJSONObject: [
|
|
41
42
|
"jpegLength": rendered.count, "width": renderer.width, "height": renderer.height,
|
|
42
43
|
"pieces": renderer.pieces, "hardware": renderer.hardware, "panel": request.panel, "hingeDegrees": request.hingeDegrees,
|