Skip to content

Commit

Permalink
Move tracking of swapchain image availability from MVKSwapchain to MV…
Browse files Browse the repository at this point in the history
…KSwapchainImage.
  • Loading branch information
billhollings committed Apr 2, 2020
1 parent 68b5f72 commit 6229582
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 171 deletions.
26 changes: 26 additions & 0 deletions MoltenVK/MoltenVK/GPUObjects/MVKImage.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,19 @@ class MVKImage : public MVKResource {
#pragma mark -
#pragma mark MVKSwapchainImage

/** Indicates the relative availability of each image in the swapchain. */
typedef struct MVKSwapchainImageAvailability {
uint64_t acquisitionID; /**< When this image was last made available, relative to the other images in the swapchain. Smaller value is earlier. */
uint32_t waitCount; /**< The number of semaphores already waiting for this image. */
bool isAvailable; /**< Indicates whether this image is currently available. */

bool operator< (const MVKSwapchainImageAvailability& rhs) const;
} MVKSwapchainImageAvailability;

/** Tracks a semaphore and fence for later signaling. */
typedef std::pair<MVKSemaphore*, MVKFence*> MVKSwapchainSignaler;


/** Represents a Vulkan image used as a rendering destination within a swapchain. */
class MVKSwapchainImage : public MVKImage {

Expand Down Expand Up @@ -315,14 +328,27 @@ class MVKSwapchainImage : public MVKImage {
~MVKSwapchainImage() override;

protected:
friend MVKSwapchain;

id<MTLTexture> newMTLTexture() override;
id<CAMetalDrawable> getCAMetalDrawable();
void resetMetalDrawable();
MVKSwapchainImageAvailability getAvailability();
void makeAvailable();
void signalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence);
void signal(MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff);
void signalPresentationSemaphore(id<MTLCommandBuffer> mtlCmdBuff);
static void markAsTracked(MVKSwapchainSignaler& signaler);
static void unmarkAsTracked(MVKSwapchainSignaler& signaler);
void renderWatermark(id<MTLCommandBuffer> mtlCmdBuff);

MVKSwapchain* _swapchain;
uint32_t _swapchainIndex;
id<CAMetalDrawable> _mtlDrawable;
MVKSwapchainImageAvailability _availability;
MVKVectorInline<MVKSwapchainSignaler, 1> _availabilitySignalers;
MVKSwapchainSignaler _preSignaler;
std::mutex _availabilityLock;
};


Expand Down
114 changes: 112 additions & 2 deletions MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,112 @@
return VK_SUCCESS;
}

bool MVKSwapchainImageAvailability::operator< (const MVKSwapchainImageAvailability& rhs) const {
if ( isAvailable && !rhs.isAvailable) { return true; }
if ( !isAvailable && rhs.isAvailable) { return false; }

if (waitCount < rhs.waitCount) { return true; }
if (waitCount > rhs.waitCount) { return false; }

return acquisitionID < rhs.acquisitionID;
}

MVKSwapchainImageAvailability MVKSwapchainImage::getAvailability() {
lock_guard<mutex> lock(_availabilityLock);

return _availability;
}

// Makes an image available for acquisition by the app.
// If any semaphores are waiting to be signaled when this image becomes available, the
// earliest semaphore is signaled, and this image remains unavailable for other uses.
void MVKSwapchainImage::makeAvailable() {
lock_guard<mutex> lock(_availabilityLock);

// Mark when this event happened, relative to that of other images
_availability.acquisitionID = _swapchain->getNextAcquisitionID();

// Mark this image as available if no semaphores or fences are waiting to be signaled.
_availability.isAvailable = _availabilitySignalers.empty();

MVKSwapchainSignaler signaler;
if (_availability.isAvailable) {
// If this image is available, signal the semaphore and fence that were associated
// with the last time this image was acquired while available. This is a workaround for
// when an app uses a single semaphore or fence for more than one swapchain image.
// Becuase the semaphore or fence will be signaled by more than one image, it will
// get out of sync, and the final use of the image would not be signaled as a result.
signaler = _preSignaler;
} else {
// If this image is not yet available, extract and signal the first semaphore and fence.
auto sigIter = _availabilitySignalers.begin();
signaler = *sigIter;
_availabilitySignalers.erase(sigIter);
}

// Signal the semaphore and fence, and let them know they are no longer being tracked.
signal(signaler, nil);
unmarkAsTracked(signaler);

// MVKLogDebug("Signaling%s swapchain image %p semaphore %p from present, with %lu remaining semaphores.", (_availability.isAvailable ? " pre-signaled" : ""), this, signaler.first, _availabilitySignalers.size());
}

void MVKSwapchainImage::signalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence) {
lock_guard<mutex> lock(_availabilityLock);

auto signaler = make_pair(semaphore, fence);
if (_availability.isAvailable) {
_availability.isAvailable = false;

// If signalling through a MTLEvent, use an ephemeral MTLCommandBuffer.
// Another option would be to use MTLSharedEvent in MVKSemaphore, but that might
// impose unacceptable performance costs to handle this particular case.
@autoreleasepool {
MVKSemaphore* mvkSem = signaler.first;
id<MTLCommandBuffer> mtlCmdBuff = (mvkSem && mvkSem->isUsingCommandEncoding()
? [_device->getAnyQueue()->getMTLCommandQueue() commandBufferWithUnretainedReferences]
: nil);
signal(signaler, mtlCmdBuff);
[mtlCmdBuff commit];
}

_preSignaler = signaler;
} else {
_availabilitySignalers.push_back(signaler);
}
markAsTracked(signaler);

// MVKLogDebug("%s swapchain image %p semaphore %p in acquire with %lu other semaphores.", (_availability.isAvailable ? "Signaling" : "Tracking"), this, semaphore, _availabilitySignalers.size());
}

// If present, signal the semaphore for the first waiter for the given image.
void MVKSwapchainImage::signalPresentationSemaphore(id<MTLCommandBuffer> mtlCmdBuff) {
lock_guard<mutex> lock(_availabilityLock);

if ( !_availabilitySignalers.empty() ) {
MVKSemaphore* mvkSem = _availabilitySignalers.front().first;
if (mvkSem) { mvkSem->encodeSignal(mtlCmdBuff); }
}
}

// Signal either or both of the semaphore and fence in the specified tracker pair.
void MVKSwapchainImage::signal(MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff) {
if (signaler.first) { signaler.first->encodeSignal(mtlCmdBuff); }
if (signaler.second) { signaler.second->signal(); }
}

// Tell the semaphore and fence that they are being tracked for future signaling.
void MVKSwapchainImage::markAsTracked(MVKSwapchainSignaler& signaler) {
if (signaler.first) { signaler.first->retain(); }
if (signaler.second) { signaler.second->retain(); }
}

// Tell the semaphore and fence that they are no longer being tracked for future signaling.
void MVKSwapchainImage::unmarkAsTracked(MVKSwapchainSignaler& signaler) {
if (signaler.first) { signaler.first->release(); }
if (signaler.second) { signaler.second->release(); }
}


#pragma mark Metal

Expand Down Expand Up @@ -853,11 +959,11 @@
if (scName) { mvkPopDebugGroup(mtlCmdBuff); }

resetMetalDrawable();
_swapchain->signalPresentationSemaphore(_swapchainIndex, mtlCmdBuff);
signalPresentationSemaphore(mtlCmdBuff);

retain(); // Ensure this image is not destroyed while awaiting MTLCommandBuffer completion
[mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) {
_swapchain->makeAvailable(_swapchainIndex);
makeAvailable();
release();
}];
}
Expand All @@ -879,6 +985,10 @@
_swapchain = swapchain;
_swapchainIndex = swapchainIndex;
_mtlDrawable = nil;

_availability.acquisitionID = _swapchain->getNextAcquisitionID();
_availability.isAvailable = true;
_preSignaler = make_pair(nullptr, nullptr);
}

MVKSwapchainImage::MVKSwapchainImage(MVKDevice* device,
Expand Down
38 changes: 3 additions & 35 deletions MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,9 @@ class MVKWatermark;
@class MVKBlockObserver;


/** Indicates the relative availability of each image in the swapchain. */
typedef struct MVKSwapchainImageAvailability {
uint64_t acquisitionID; /**< When this image was last made available, relative to the other images in the swapchain. Smaller value is earlier. */
uint32_t waitCount; /**< The number of semaphores already waiting for this image. */
bool isAvailable; /**< Indicates whether this image is currently available. */

bool operator< (const MVKSwapchainImageAvailability& rhs) const;
} MVKSwapchainImageAvailability;


#pragma mark -
#pragma mark MVKSwapchain

/** Tracks a semaphore and fence for later signaling. */
typedef std::pair<MVKSemaphore*, MVKFence*> MVKSwapchainSignaler;

/** Represents a Vulkan swapchain. */
class MVKSwapchain : public MVKVulkanAPIDeviceObject {

Expand All @@ -54,10 +42,10 @@ class MVKSwapchain : public MVKVulkanAPIDeviceObject {
VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT; }

/** Returns the number of images in this swapchain. */
uint32_t getImageCount();
inline uint32_t getImageCount() { return (uint32_t)_surfaceImages.size(); }

/** Returns the image at the specified index. */
MVKSwapchainImage* getImage(uint32_t index);
inline MVKSwapchainImage* getImage(uint32_t index) { return _surfaceImages[index]; }

/**
* Returns the array of presentable images associated with this swapchain.
Expand Down Expand Up @@ -93,13 +81,6 @@ class MVKSwapchain : public MVKVulkanAPIDeviceObject {
/** Adds HDR metadata to this swapchain. */
void setHDRMetadataEXT(const VkHdrMetadataEXT& metadata);

/**
* Registers a semaphore and/or fence that will be signaled when the image at the given index becomes available.
* This function accepts both a semaphore and a fence, and either none, one, or both may be provided.
* If this image is available already, the semaphore and fence are immediately signaled.
*/
void signalWhenAvailable(uint32_t imageIndex, MVKSemaphore* semaphore, MVKFence* fence);


#pragma mark Construction

Expand All @@ -110,12 +91,6 @@ class MVKSwapchain : public MVKVulkanAPIDeviceObject {
protected:
friend class MVKSwapchainImage;

struct Availability {
MVKSwapchainImageAvailability status;
MVKVectorInline<MVKSwapchainSignaler, 1> signalers;
MVKSwapchainSignaler preSignaled;
};

void propogateDebugName() override;
void initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo, uint32_t imgCnt);
void initSurfaceImages(const VkSwapchainCreateInfoKHR* pCreateInfo, uint32_t imgCnt);
Expand All @@ -125,17 +100,10 @@ class MVKSwapchain : public MVKVulkanAPIDeviceObject {
void willPresentSurface(id<MTLTexture> mtlTexture, id<MTLCommandBuffer> mtlCmdBuff);
void renderWatermark(id<MTLTexture> mtlTexture, id<MTLCommandBuffer> mtlCmdBuff);
void markFrameInterval();
void signal(MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff);
void signalPresentationSemaphore(uint32_t imgIdx, id<MTLCommandBuffer> mtlCmdBuff);
static void markAsTracked(MVKSwapchainSignaler& signaler);
static void unmarkAsTracked(MVKSwapchainSignaler& signaler);
void makeAvailable(uint32_t imgIdx);

CAMetalLayer* _mtlLayer;
MVKWatermark* _licenseWatermark;
MVKVectorInline<MVKSwapchainImage*, kMVKMaxSwapchainImageCount> _surfaceImages;
MVKVectorInline<Availability, kMVKMaxSwapchainImageCount> _imageAvailability;
std::mutex _availabilityLock;
std::atomic<uint64_t> _currentAcquisitionID;
CGSize _mtlLayerOrigDrawSize;
MVKSwapchainPerformance _performanceStatistics;
Expand Down
Loading

0 comments on commit 6229582

Please sign in to comment.