10 유니티 게임 애플 화면 미러링시 크래시
A. 증상
아이폰/아이패드에서 AirPlay 활성화 후 게임 실행 했을 때 크래시 발생(스플래시 스크린 전 종료)
iOS 11에서만 발생
Stack trace: "F-1) 크래시 Stack trace" 참고
B. 재현방법
아이폰/아이패드에서 "화면 미러링" 선택
AirPlay를 이용해 애플 TV와 연결
유니티 게임 실행
게임 시작하자마자 크래시 발생
C. 원인
iOS 11에서 외부 모니터가 연결되었을 때 게임 시작 시 "UIScreenDidConnectNotification" 알림이 이전 iOS 버전보다 예상치 못하게 빨리 호출되어 발생함
D. 해결방법
공식패치 적용
2017.1.3p1, 2017.2.1p2, 2017.3.0p2: iOS: Fixed iOS 11 crash when application is launched from URL and airplay screen mirroring is enabled.
5.6은 추후 릴리즈 예정
Workaround 적용
공식패치 사용이 어려울 경우 다음의 workaround를 적용
유니티 설치폴더 또는 Xcode의
DisplayManager.mm을 "F-2) DisplayManager.mm 수정 부분"을 참고하여 수정(해당 코드 부분 주석 처리)
-> 유니티 설치폴더(iOS 빌드 전): Unity/PlaybackEngines/iOSSupport/Trampoline/Classes/Unity/DisplayManager.mm
-> 프로젝트 당 생성된 Xcode: ProjectRoot/Classes/Unity/DisplayManager.mm
아이폰/아이패드와 외부 모니터를 동시에 사용하는 경우 게임이 정상 작동하지 않을 수 있음(미러링은 정상 작동함)
E. 테스트 결과
테스트 기기: iPhone 5(iOS 11.2.6), iPhone 6(iOS 11.2.1), iPhone X(iOS 11.2.2)
테스트 버전: Unity 5.4, 5.5, 5.6(수정 부분은 Unity 4.7를 포함한 5.3 이하 버전에서도 동일함, 4.6 이하 버전은 확인 필요)
AirPlay 뿐만 아니라 라이팅-HDMI 어탭터 연결을 통한 미러링이 정상 작동함
F. 참고
크래시 Stack trace
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x88)
* frame #0: 0x000000010494ce20 remotear`::UnityUpdateDisplayList() at LibEntryPoint.mm:614 [opt]
frame #1: 0x00000001046c7148 remotear`::-DisplayManager updateDisplayListInUnity at DisplayManager.mm:338 [opt]
frame #2: 0x00000001046c74ec remotear`::-DisplayManager screenDidConnect: at DisplayManager.mm:377 [opt]
frame #3: 0x000000018305e12c CoreFoundation`__CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20
frame #4: 0x000000018305d6cc CoreFoundation`_CFXRegistrationPost + 420
frame #5: 0x000000018305d430 CoreFoundation`___CFXNotificationPost_block_invoke + 60
frame #6: 0x00000001830da9f4 CoreFoundation`-[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1408
frame #7: 0x0000000182f943e0 CoreFoundation`_CFXNotificationPost + 380
frame #8: 0x00000001839b4498 Foundation`-[NSNotificationCenter postNotificationName:object:userInfo:] + 68
frame #9: 0x000000018ca0a7f8 UIKit`+[UIScreen _FBSDisplayConfigurationConnected:andNotify:] + 280
frame #10: 0x000000018c740d30 UIKit`-[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 292
frame #11: 0x000000018cb446ec UIKit`-[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 364
frame #12: 0x000000018576d768 FrontBoardServices`-[FBSSceneImpl _didCreateWithTransitionContext:completion:] + 364
frame #13: 0x0000000185776070 FrontBoardServices`__56-[FBSWorkspace client:handleCreateScene:withCompletion:]_block_invoke_2 + 224
frame #14: 0x0000000182a51048 libdispatch.dylib`_dispatch_client_callout + 16
frame #15: 0x0000000182a586c8 libdispatch.dylib`_dispatch_block_invoke_direct$VARIANT$mp + 288
frame #16: 0x00000001857a1a04 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 36
frame #17: 0x00000001857a16a8 FrontBoardServices`-[FBSSerialQueue _performNext] + 404
frame #18: 0x00000001857a1c44 FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 56
frame #19: 0x0000000183074358 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24
frame #20: 0x00000001830742d8 CoreFoundation`__CFRunLoopDoSource0 + 88
frame #21: 0x0000000183073b60 CoreFoundation`__CFRunLoopDoSources0 + 204
frame #22: 0x0000000183071738 CoreFoundation`__CFRunLoopRun + 1048
frame #23: 0x0000000182f922d8 CoreFoundation`CFRunLoopRunSpecific + 436
frame #24: 0x0000000184e23f84 GraphicsServices`GSEventRunModal + 100
frame #25: 0x000000018c53e880 UIKit`UIApplicationMain + 208
frame #26: 0x00000001046b9f00 remotear`main(argc=1, argv=0x000000016b74bb50) at main.mm:33 [opt]
frame #27: 0x0000000182ab656c libdyld.dylib`start + 4 |
2. DisplayManager.mm 수정 부분
수정전 - (id)init
{
if ((self = [super init]))
{
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(screenDidConnect:)
name: UIScreenDidConnectNotification
object: nil
];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(screenDidDisconnect:)
name: UIScreenDidDisconnectNotification
object: nil
];
_displayConnection = [NSMapTable
mapTableWithKeyOptions: NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality
valueOptions: NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality
];
for (UIScreen* screen in[UIScreen screens])
[self registerScreen: screen];
_mainDisplay = self[[UIScreen mainScreen]];
}
return self;
} |
수정후 - (id)init
{
if ((self = [super init]))
{
/*
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(screenDidConnect:)
name: UIScreenDidConnectNotification
object: nil
];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(screenDidDisconnect:)
name: UIScreenDidDisconnectNotification
object: nil
];
*/
_displayConnection = [NSMapTable
mapTableWithKeyOptions: NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality
valueOptions: NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality
];
for (UIScreen* screen in[UIScreen screens])
[self registerScreen: screen];
_mainDisplay = self[[UIScreen mainScreen]];
}
return self;
} |