Move Metal drawable presentation from MTLCommandBuffer to MTLDrawable.
Update MVKPresentableSwapchainImage::presentCAMetalDrawable() to create a MTLCommandBuffer scheduled-handler and present the MTLDrawable from there. According to Apple, it is more performant to call MTLDrawable present from within a MTLCommandBuffer scheduled-handler than it is to call MTLCommandBuffer presentDrawable:. Pass presentation timing info as a struct to simplify calls.
This commit is contained in:
parent
766e05b70c
commit
1b4c045707
@ -21,6 +21,8 @@ Released TBD
|
|||||||
- Use `VK_KHR_image_format_list` to disable `MTLTextureUsagePixelFormatView`
|
- Use `VK_KHR_image_format_list` to disable `MTLTextureUsagePixelFormatView`
|
||||||
if only swizzles or `sRGB` conversion will be used for image views, improving
|
if only swizzles or `sRGB` conversion will be used for image views, improving
|
||||||
performance on *iOS* by allowing Metal to use lossless texture compression.
|
performance on *iOS* by allowing Metal to use lossless texture compression.
|
||||||
|
- Move *Metal* drawable presentation from `MTLCommandBuffer` to `MTLDrawable`
|
||||||
|
to improve performance and reduce blocking.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -429,6 +429,14 @@ typedef struct MVKSwapchainImageAvailability {
|
|||||||
bool operator< (const MVKSwapchainImageAvailability& rhs) const;
|
bool operator< (const MVKSwapchainImageAvailability& rhs) const;
|
||||||
} MVKSwapchainImageAvailability;
|
} MVKSwapchainImageAvailability;
|
||||||
|
|
||||||
|
/** VK_GOOGLE_display_timing extension info */
|
||||||
|
typedef struct {
|
||||||
|
MVKPresentableSwapchainImage* presentableImage;
|
||||||
|
bool hasPresentTime; // Keep track of whether presentation included VK_GOOGLE_display_timing
|
||||||
|
uint32_t presentID; // VK_GOOGLE_display_timing presentID
|
||||||
|
uint64_t desiredPresentTime; // VK_GOOGLE_display_timing desired presentation time in nanoseconds
|
||||||
|
} MVKPresentTimingInfo;
|
||||||
|
|
||||||
/** Tracks a semaphore and fence for later signaling. */
|
/** Tracks a semaphore and fence for later signaling. */
|
||||||
typedef std::pair<MVKSemaphore*, MVKFence*> MVKSwapchainSignaler;
|
typedef std::pair<MVKSemaphore*, MVKFence*> MVKSwapchainSignaler;
|
||||||
|
|
||||||
@ -440,15 +448,9 @@ public:
|
|||||||
|
|
||||||
#pragma mark Metal
|
#pragma mark Metal
|
||||||
|
|
||||||
/**
|
/** Presents the contained drawable to the OS. */
|
||||||
* Presents the contained drawable to the OS, releases the Metal drawable and its
|
void presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff,
|
||||||
* texture back to the Metal layer's pool, and makes the image memory available for new use.
|
MVKPresentTimingInfo presentTimingInfo);
|
||||||
*
|
|
||||||
* If mtlCmdBuff is not nil, the contained drawable is scheduled for presentation using
|
|
||||||
* the presentDrawable: method of the command buffer. If mtlCmdBuff is nil, the contained
|
|
||||||
* drawable is presented immediately using the present method of the drawable.
|
|
||||||
*/
|
|
||||||
void presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff, bool hasPresentTime, uint32_t presentID, uint64_t desiredPresentTime);
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Construction
|
#pragma mark Construction
|
||||||
@ -465,6 +467,7 @@ protected:
|
|||||||
friend MVKSwapchain;
|
friend MVKSwapchain;
|
||||||
|
|
||||||
id<CAMetalDrawable> getCAMetalDrawable() override;
|
id<CAMetalDrawable> getCAMetalDrawable() override;
|
||||||
|
void presentCAMetalDrawable(MVKPresentTimingInfo presentTimingInfo);
|
||||||
void releaseMetalDrawable();
|
void releaseMetalDrawable();
|
||||||
MVKSwapchainImageAvailability getAvailability();
|
MVKSwapchainImageAvailability getAvailability();
|
||||||
void makeAvailable();
|
void makeAvailable();
|
||||||
|
@ -1285,52 +1285,53 @@ id<CAMetalDrawable> MVKPresentableSwapchainImage::getCAMetalDrawable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Present the drawable and make myself available only once the command buffer has completed.
|
// Present the drawable and make myself available only once the command buffer has completed.
|
||||||
void MVKPresentableSwapchainImage::presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff, bool hasPresentTime, uint32_t presentID, uint64_t desiredPresentTime) {
|
void MVKPresentableSwapchainImage::presentCAMetalDrawable(id<MTLCommandBuffer> mtlCmdBuff,
|
||||||
|
MVKPresentTimingInfo presentTimingInfo) {
|
||||||
|
|
||||||
_swapchain->willPresentSurface(getMTLTexture(0), mtlCmdBuff);
|
_swapchain->willPresentSurface(getMTLTexture(0), mtlCmdBuff);
|
||||||
|
|
||||||
NSString* scName = _swapchain->getDebugName();
|
[mtlCmdBuff addScheduledHandler: ^(id<MTLCommandBuffer> mcb) {
|
||||||
if (scName) { mvkPushDebugGroup(mtlCmdBuff, scName); }
|
presentCAMetalDrawable(presentTimingInfo);
|
||||||
if (!hasPresentTime) {
|
}];
|
||||||
[mtlCmdBuff presentDrawable: getCAMetalDrawable()];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Convert from nsecs to seconds
|
|
||||||
CFTimeInterval presentTimeSeconds = ( double ) desiredPresentTime * 1.0e-9;
|
|
||||||
[mtlCmdBuff presentDrawable: getCAMetalDrawable() atTime:(CFTimeInterval)presentTimeSeconds];
|
|
||||||
}
|
|
||||||
if (scName) { mvkPopDebugGroup(mtlCmdBuff); }
|
|
||||||
|
|
||||||
signalPresentationSemaphore(mtlCmdBuff);
|
// Ensure this image is not destroyed while awaiting MTLCommandBuffer completion
|
||||||
|
retain();
|
||||||
retain(); // Ensure this image is not destroyed while awaiting MTLCommandBuffer completion
|
|
||||||
[mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) {
|
[mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) {
|
||||||
makeAvailable();
|
makeAvailable();
|
||||||
release();
|
release();
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (hasPresentTime) {
|
signalPresentationSemaphore(mtlCmdBuff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If MTLDrawable.presentedTime/addPresentedHandler isn't supported.
|
||||||
|
// Treat it as if the present happened when requested.
|
||||||
|
void MVKPresentableSwapchainImage::presentCAMetalDrawable(MVKPresentTimingInfo presentTimingInfo) {
|
||||||
|
|
||||||
|
id<CAMetalDrawable> mtlDrwbl = getCAMetalDrawable();
|
||||||
|
|
||||||
|
if (presentTimingInfo.hasPresentTime) {
|
||||||
|
|
||||||
|
// Convert from nsecs to seconds for Metal
|
||||||
|
[mtlDrwbl presentAtTime: (double)presentTimingInfo.desiredPresentTime * 1.0e-9];
|
||||||
|
|
||||||
#if MVK_OS_SIMULATOR
|
#if MVK_OS_SIMULATOR
|
||||||
// If MTLDrawable.presentedTime/addPresentedHandler isn't supported, just treat it as if the
|
_swapchain->recordPresentTime(presentTimingInfo);
|
||||||
// present happened when requested
|
|
||||||
_swapchain->recordPresentTime(presentID, desiredPresentTime, desiredPresentTime);
|
|
||||||
#else
|
#else
|
||||||
if ([_mtlDrawable respondsToSelector: @selector(addPresentedHandler:)]) {
|
if ([mtlDrwbl respondsToSelector: @selector(addPresentedHandler:)]) {
|
||||||
retain(); // Ensure this image is not destroyed while awaiting presentation
|
// Ensure this image is not destroyed while awaiting presentation
|
||||||
[_mtlDrawable addPresentedHandler: ^(id<MTLDrawable> drawable) {
|
retain();
|
||||||
// Record the presentation time
|
[mtlDrwbl addPresentedHandler: ^(id<MTLDrawable> drawable) {
|
||||||
CFTimeInterval presentedTimeSeconds = drawable.presentedTime;
|
_swapchain->recordPresentTime(presentTimingInfo, drawable.presentedTime * 1.0e9);
|
||||||
uint64_t presentedTimeNanoseconds = (uint64_t)(presentedTimeSeconds * 1.0e9);
|
|
||||||
_swapchain->recordPresentTime(presentID, desiredPresentTime, presentedTimeNanoseconds);
|
|
||||||
release();
|
release();
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
// If MTLDrawable.presentedTime/addPresentedHandler isn't supported, just treat it as if the
|
_swapchain->recordPresentTime(presentTimingInfo);
|
||||||
// present happened when requested
|
|
||||||
_swapchain->recordPresentTime(presentID, desiredPresentTime, desiredPresentTime);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
} else {
|
||||||
|
[mtlDrwbl present];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resets the MTLTexture and CAMetalDrawable underlying this image.
|
// Resets the MTLTexture and CAMetalDrawable underlying this image.
|
||||||
|
@ -255,13 +255,6 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
id<MTLCommandBuffer> getMTLCommandBuffer();
|
id<MTLCommandBuffer> getMTLCommandBuffer();
|
||||||
|
|
||||||
typedef struct {
|
MVKSmallVector<MVKPresentTimingInfo, 4> _presentInfo;
|
||||||
MVKPresentableSwapchainImage* presentableImage;
|
|
||||||
bool hasPresentTime; // Keep track of whether present included VK_GOOGLE_display_timing
|
|
||||||
uint32_t presentID; // VK_GOOGLE_display_timing presentID
|
|
||||||
uint64_t desiredPresentTime; // VK_GOOGLE_display_timing desired presentation time in nanoseconds
|
|
||||||
} PresentInfo;
|
|
||||||
|
|
||||||
MVKSmallVector<PresentInfo, 4> _presentInfo;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -353,7 +353,7 @@ void MVKQueuePresentSurfaceSubmission::execute() {
|
|||||||
for (auto& ws : _waitSemaphores) { ws->encodeWait(mtlCmdBuff); }
|
for (auto& ws : _waitSemaphores) { ws->encodeWait(mtlCmdBuff); }
|
||||||
for (int i = 0; i < _presentInfo.size(); i++ ) {
|
for (int i = 0; i < _presentInfo.size(); i++ ) {
|
||||||
MVKPresentableSwapchainImage *img = _presentInfo[i].presentableImage;
|
MVKPresentableSwapchainImage *img = _presentInfo[i].presentableImage;
|
||||||
img->presentCAMetalDrawable(mtlCmdBuff, _presentInfo[i].hasPresentTime, _presentInfo[i].presentID, _presentInfo[i].desiredPresentTime);
|
img->presentCAMetalDrawable(mtlCmdBuff, _presentInfo[i]);
|
||||||
}
|
}
|
||||||
for (auto& ws : _waitSemaphores) { ws->encodeWait(nil); }
|
for (auto& ws : _waitSemaphores) { ws->encodeWait(nil); }
|
||||||
[mtlCmdBuff commit];
|
[mtlCmdBuff commit];
|
||||||
@ -401,7 +401,7 @@ MVKQueuePresentSurfaceSubmission::MVKQueuePresentSurfaceSubmission(MVKQueue* que
|
|||||||
_presentInfo.reserve(scCnt);
|
_presentInfo.reserve(scCnt);
|
||||||
for (uint32_t scIdx = 0; scIdx < scCnt; scIdx++) {
|
for (uint32_t scIdx = 0; scIdx < scCnt; scIdx++) {
|
||||||
MVKSwapchain* mvkSC = (MVKSwapchain*)pPresentInfo->pSwapchains[scIdx];
|
MVKSwapchain* mvkSC = (MVKSwapchain*)pPresentInfo->pSwapchains[scIdx];
|
||||||
PresentInfo presentInfo = {};
|
MVKPresentTimingInfo presentInfo = {};
|
||||||
presentInfo.presentableImage = mvkSC->getPresentableImage(pPresentInfo->pImageIndices[scIdx]);
|
presentInfo.presentableImage = mvkSC->getPresentableImage(pPresentInfo->pImageIndices[scIdx]);
|
||||||
if ( pPresentTimesGOOGLE ) {
|
if ( pPresentTimesGOOGLE ) {
|
||||||
presentInfo.hasPresentTime = true;
|
presentInfo.hasPresentTime = true;
|
||||||
|
@ -113,7 +113,7 @@ protected:
|
|||||||
void willPresentSurface(id<MTLTexture> mtlTexture, id<MTLCommandBuffer> mtlCmdBuff);
|
void willPresentSurface(id<MTLTexture> mtlTexture, id<MTLCommandBuffer> mtlCmdBuff);
|
||||||
void renderWatermark(id<MTLTexture> mtlTexture, id<MTLCommandBuffer> mtlCmdBuff);
|
void renderWatermark(id<MTLTexture> mtlTexture, id<MTLCommandBuffer> mtlCmdBuff);
|
||||||
void markFrameInterval();
|
void markFrameInterval();
|
||||||
void recordPresentTime(uint32_t presentID, uint64_t desiredPresentTime, uint64_t actualPresentTime);
|
void recordPresentTime(MVKPresentTimingInfo presentTimingInfo, uint64_t actualPresentTime = 0);
|
||||||
|
|
||||||
CAMetalLayer* _mtlLayer;
|
CAMetalLayer* _mtlLayer;
|
||||||
MVKWatermark* _licenseWatermark;
|
MVKWatermark* _licenseWatermark;
|
||||||
|
@ -434,7 +434,7 @@ VkResult MVKSwapchain::getPastPresentationTiming(uint32_t *pCount, VkPastPresent
|
|||||||
return VK_SUCCESS;
|
return VK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MVKSwapchain::recordPresentTime(uint32_t presentID, uint64_t desiredPresentTime, uint64_t actualPresentTime) {
|
void MVKSwapchain::recordPresentTime(MVKPresentTimingInfo presentTimingInfo, uint64_t actualPresentTime) {
|
||||||
std::lock_guard<std::mutex> lock(_presentHistoryLock);
|
std::lock_guard<std::mutex> lock(_presentHistoryLock);
|
||||||
if (_presentHistoryCount < kMaxPresentationHistory) {
|
if (_presentHistoryCount < kMaxPresentationHistory) {
|
||||||
_presentHistoryCount++;
|
_presentHistoryCount++;
|
||||||
@ -442,8 +442,12 @@ void MVKSwapchain::recordPresentTime(uint32_t presentID, uint64_t desiredPresent
|
|||||||
} else {
|
} else {
|
||||||
_presentHistoryHeadIndex = (_presentHistoryHeadIndex + 1) % kMaxPresentationHistory;
|
_presentHistoryHeadIndex = (_presentHistoryHeadIndex + 1) % kMaxPresentationHistory;
|
||||||
}
|
}
|
||||||
_presentTimingHistory[_presentHistoryIndex].presentID = presentID;
|
|
||||||
_presentTimingHistory[_presentHistoryIndex].desiredPresentTime = desiredPresentTime;
|
// If actual time not supplied, use desired time instead
|
||||||
|
if (actualPresentTime == 0) { actualPresentTime = presentTimingInfo.desiredPresentTime; }
|
||||||
|
|
||||||
|
_presentTimingHistory[_presentHistoryIndex].presentID = presentTimingInfo.presentID;
|
||||||
|
_presentTimingHistory[_presentHistoryIndex].desiredPresentTime = presentTimingInfo.desiredPresentTime;
|
||||||
_presentTimingHistory[_presentHistoryIndex].actualPresentTime = actualPresentTime;
|
_presentTimingHistory[_presentHistoryIndex].actualPresentTime = actualPresentTime;
|
||||||
// These details are not available in Metal
|
// These details are not available in Metal
|
||||||
_presentTimingHistory[_presentHistoryIndex].earliestPresentTime = actualPresentTime;
|
_presentTimingHistory[_presentHistoryIndex].earliestPresentTime = actualPresentTime;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user