This is an example where everything is complete well before vsync and yet everything just decides to wait for another 8ms. The encoder and the GPU work is completing on the verge of 8 ms. This happens regularly.
In the examples given, the encoder is running and the frame starts / ends when the command buffer is committed, but if you have the vsync enabled but the sync is to get the drawable, then the encoder has to eat up GPU time to be done by the next vsync.
If I go to three drawables to "fix" this timing issue, then it holds back a frame rather than going as early as possible.
I am currently call presentDrawableAfterMinimumDuration with a 4ms duration to try and trick it to release early, but it doesn't seem to make much of a difference
Post
Replies
Boosts
Views
Activity
Just if it wasn't clear - I want to lock to VSync because that is optimal from a latency point of view - it should be a guarantee of the smallest time from input to screen because you are synchronising the update code to it.
I am not running decoupled deliberately to have the smallest latency possible
I would use the CAMetalDisplayLink, but when I adapt the example code for it I get similar issues - it seems like the fullscreen mode management of the drawables is not doing what I would expect.
I suspect that the bugs come from setting the display sync which is somehow using up one of the drawables (through not releasing at the right time) interacting with whatever code decides how long to display the current frame.
The two things seem to influence each other in bad ways. For example, I would have expected that if I pack both CPU and GPU time added together down to sub 8ms, then the very next vsync, it should show and release the drawable for the next frame. But that is not happening and the drawable is being held on to and then the timer which is trying to manage the ProMotion thinks things have gone on too long and hold the frame time too.
I suspect that the display sync enabled code path just has not been tested for 120hz properly when there is more than nothing going on, but way less than 8ms worth.
The CAMetalDisplayLink also does not seem to like the Sync being enabled especially if you have an external monitor attached - it can't seem to tell which monitor has which Vsync
In fact, I get the same behaviour if I modify the LearnMetalCPP texturing example to allow the window to be resizeable, set the preferred framerate to 120 and make the num frames in flight in two places to be 2 rather than 3
void MyAppDelegate::applicationDidFinishLaunching( NS::Notification* pNotification )
{
CGRect frame = (CGRect){ {100.0, 100.0}, {1024.0, 1024.0} };
_pWindow = NS::Window::alloc()->init(
frame,
NS::WindowStyleMaskClosable|NS::WindowStyleMaskTitled | NS::WindowStyleMaskResizable,
NS::BackingStoreBuffered,
false );
_pDevice = MTL::CreateSystemDefaultDevice();
_pMtkView = MTK::View::alloc()->init( frame, _pDevice );
_pMtkView->setColorPixelFormat( MTL::PixelFormat::PixelFormatBGRA8Unorm_sRGB );
_pMtkView->setClearColor( MTL::ClearColor::Make( 0.1, 0.1, 0.1, 1.0 ) );
_pMtkView->setDepthStencilPixelFormat( MTL::PixelFormat::PixelFormatDepth16Unorm );
_pMtkView->setClearDepth( 1.0f );
_pMtkView->setPreferredFramesPerSecond(120);
_pViewDelegate = new MyMTKViewDelegate( _pDevice );
_pMtkView->setDelegate( _pViewDelegate );
_pWindow->setContentView( _pMtkView );
_pWindow->setTitle( NS::String::string( "07 - Texture Mapping", NS::StringEncoding::UTF8StringEncoding ) );
_pWindow->makeKeyAndOrderFront( nullptr );
NS::Application* pApp = reinterpret_cast< NS::Application* >( pNotification->object() );
pApp->activateIgnoringOtherApps( true );
}
static constexpr size_t kMaxFramesInFlight = 2;
const int Renderer::kMaxFramesInFlight = 2;
I get 80Hz on average rather than reliably 120Hz when I switch into fullscreen
Ok - I worked it out for myself - I needed to add an Apple Clang build rule for the .S files and not try to add them to the Compiled Sources directly...