123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- #include "UnityAppController+Rendering.h"
- #include "UnityAppController+ViewHandling.h"
- #include "Unity/InternalProfiler.h"
- #include "Unity/UnityMetalSupport.h"
- #include "Unity/DisplayManager.h"
- #include "Unity/EAGLContextHelper.h"
- #include "UI/UnityView.h"
- #include <dlfcn.h>
- // On some devices presenting render buffer may sporadically take long time to complete even with very simple scenes.
- // In these cases display link still fires at steady frame rate but input processing becomes stuttering.
- // As a workaround this switch disables display link during rendering a frame.
- // If you are running a GPU bound scene and experience frame drop you may want to disable this switch.
- #define ENABLE_DISPLAY_LINK_PAUSING 1
- #define ENABLE_RUNLOOP_ACCEPT_INPUT 1
- // _glesContextCreated was renamed to _renderingInited
- extern bool _renderingInited;
- extern bool _unityAppReady;
- extern bool _skipPresent;
- extern bool _didResignActive;
- static int _renderingAPI = 0;
- static int SelectRenderingAPIImpl();
- static bool _enableRunLoopAcceptInput = false;
- @implementation UnityAppController (Rendering)
- - (void)createDisplayLink
- {
- _displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(repaintDisplayLink)];
- [self callbackFramerateChange: -1];
- [_displayLink addToRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes];
- }
- - (void)destroyDisplayLink
- {
- [_displayLink invalidate];
- _displayLink = nil;
- }
- - (void)processTouchEvents
- {
- // On multicore devices running at 60 FPS some touch event delivery isn't properly interleaved with graphical frames.
- // Running additional run loop here improves event handling in those cases.
- // Passing here an NSDate from the past invokes run loop only once.
- #if ENABLE_RUNLOOP_ACCEPT_INPUT
- // We get "NSInternalInconsistencyException: unexpected start state" exception if there are events queued and app is
- // going to background at the same time. This happens when we render additional frame after receiving
- // applicationWillResignActive. So check if we are supposed to ignore input.
- bool ignoreInput = [[UIApplication sharedApplication] isIgnoringInteractionEvents];
- if (!ignoreInput && _enableRunLoopAcceptInput)
- {
- static NSDate* past = [NSDate dateWithTimeIntervalSince1970: 0]; // the oldest date we can get
- [[NSRunLoop currentRunLoop] acceptInputForMode: NSDefaultRunLoopMode beforeDate: past];
- }
- #endif
- }
- - (void)repaintDisplayLink
- {
- #if ENABLE_DISPLAY_LINK_PAUSING
- _displayLink.paused = YES;
- #endif
- if (!_didResignActive)
- {
- [self repaint];
- [self processTouchEvents];
- }
- #if ENABLE_DISPLAY_LINK_PAUSING
- _displayLink.paused = NO;
- #endif
- }
- - (void)repaint
- {
- #if UNITY_SUPPORT_ROTATION
- [self checkOrientationRequest];
- #endif
- [_unityView recreateRenderingSurfaceIfNeeded];
- [_unityView processKeyboard];
- UnityDeliverUIEvents();
- if (!UnityIsPaused())
- UnityRepaint();
- }
- - (void)callbackGfxInited
- {
- InitRendering();
- _renderingInited = true;
- [self shouldAttachRenderDelegate];
- [_renderDelegate mainDisplayInited: _mainDisplay.surface];
- [_unityView recreateRenderingSurface];
- _mainDisplay.surface->allowScreenshot = 1;
- }
- - (void)callbackPresent:(const UnityFrameStats*)frameStats
- {
- if (_skipPresent || _didResignActive)
- return;
- // metal needs special processing, because in case of airplay we need extra command buffers to present non-main screen drawables
- if (UnitySelectedRenderingAPI() == apiMetal)
- {
- #if UNITY_CAN_USE_METAL
- [[DisplayManager Instance].mainDisplay present];
- [[DisplayManager Instance] enumerateNonMainDisplaysWithBlock:^(DisplayConnection* conn) {
- PreparePresentNonMainScreenMTL((UnityDisplaySurfaceMTL*)conn.surface);
- }];
- #endif
- }
- else
- {
- [[DisplayManager Instance] present];
- }
- Profiler_FramePresent(frameStats);
- }
- - (void)callbackFramerateChange:(int)targetFPS
- {
- int maxFPS = (int)[UIScreen mainScreen].maximumFramesPerSecond;
- if (targetFPS <= 0)
- targetFPS = UnityGetTargetFPS();
- if (targetFPS > maxFPS)
- targetFPS = maxFPS;
- _enableRunLoopAcceptInput = (targetFPS == maxFPS && UnityDeviceCPUCount() > 1);
- _displayLink.preferredFramesPerSecond = targetFPS;
- }
- - (void)selectRenderingAPI
- {
- NSAssert(_renderingAPI == 0, @"[UnityAppController selectRenderingApi] called twice");
- _renderingAPI = SelectRenderingAPIImpl();
- }
- - (UnityRenderingAPI)renderingAPI
- {
- NSAssert(_renderingAPI != 0, @"[UnityAppController renderingAPI] called before [UnityAppController selectRenderingApi]");
- return (UnityRenderingAPI)_renderingAPI;
- }
- @end
- extern "C" void UnityGfxInitedCallback()
- {
- [GetAppController() callbackGfxInited];
- }
- extern "C" void UnityPresentContextCallback(struct UnityFrameStats const* unityFrameStats)
- {
- [GetAppController() callbackPresent: unityFrameStats];
- }
- extern "C" void UnityFramerateChangeCallback(int targetFPS)
- {
- [GetAppController() callbackFramerateChange: targetFPS];
- }
- extern "C" void UnityInitMainScreenRenderingCallback()
- {
- [GetAppController().mainDisplay initRendering];
- }
- static NSBundle* _MetalBundle = nil;
- static id<MTLDevice> _MetalDevice = nil;
- static EAGLContext* _GlesContext = nil;
- static bool IsMetalSupported(int /*api*/)
- {
- _MetalBundle = [NSBundle bundleWithPath: @"/System/Library/Frameworks/Metal.framework"];
- if (_MetalBundle)
- {
- [_MetalBundle load];
- _MetalDevice = ((MTLCreateSystemDefaultDeviceFunc)::dlsym(dlopen(0, RTLD_LOCAL | RTLD_LAZY), "MTLCreateSystemDefaultDevice"))();
- if (_MetalDevice)
- return true;
- }
- [_MetalBundle unload];
- return false;
- }
- static bool IsGlesSupported(int api)
- {
- _GlesContext = [[EAGLContext alloc] initWithAPI: (EAGLRenderingAPI)api];
- return _GlesContext != nil;
- }
- typedef bool(*CheckSupportedFunc)(int);
- static int SelectRenderingAPIImpl()
- {
- // Get list of graphics APIs to try from player settings
- const int kMaxAPIs = 3;
- int apis[kMaxAPIs];
- const int apiCount = UnityGetRenderingAPIs(kMaxAPIs, apis);
- // Go over them and try each
- for (int i = 0; i < apiCount; ++i)
- {
- int api = apis[i];
- // Metal
- if (api == apiMetal)
- {
- #if UNITY_CAN_USE_METAL
- if (!IsMetalSupported(0))
- continue;
- return api;
- #else
- continue;
- #endif
- }
- // GLES3
- if (api == apiOpenGLES3)
- {
- if (!IsGlesSupported(kEAGLRenderingAPIOpenGLES3))
- continue;
- return api;
- }
- // GLES2
- if (api == apiOpenGLES2)
- {
- if (!IsGlesSupported(kEAGLRenderingAPIOpenGLES2))
- continue;
- return api;
- }
- }
- return 0;
- }
- extern "C" NSBundle* UnityGetMetalBundle()
- {
- return _MetalBundle;
- }
- extern "C" MTLDeviceRef UnityGetMetalDevice() { return _MetalDevice; }
- extern "C" MTLCommandQueueRef UnityGetMetalCommandQueue() { return ((UnityDisplaySurfaceMTL*)GetMainDisplaySurface())->commandQueue; }
- extern "C" MTLCommandQueueRef UnityGetMetalDrawableCommandQueue() { return ((UnityDisplaySurfaceMTL*)GetMainDisplaySurface())->drawableCommandQueue; }
- extern "C" EAGLContext* UnityGetDataContextEAGL()
- {
- return _GlesContext;
- }
- extern "C" int UnitySelectedRenderingAPI() { return _renderingAPI; }
- extern "C" UnityRenderBufferHandle UnityBackbufferColor() { return GetMainDisplaySurface()->unityColorBuffer; }
- extern "C" UnityRenderBufferHandle UnityBackbufferDepth() { return GetMainDisplaySurface()->unityDepthBuffer; }
- extern "C" void DisplayManagerEndFrameRendering() { [[DisplayManager Instance] endFrameRendering]; }
- extern "C" void UnityPrepareScreenshot() { UnitySetRenderTarget(GetMainDisplaySurface()->unityColorBuffer, GetMainDisplaySurface()->unityDepthBuffer); }
- extern "C" void UnityRepaint()
- {
- @autoreleasepool
- {
- // this will handle running on metal just fine (nop)
- EAGLContextSetCurrentAutoRestore autorestore(GetMainDisplaySurface());
- Profiler_FrameStart();
- UnityInputProcess();
- UnityPlayerLoop();
- Profiler_FrameEnd();
- }
- }
|