Fix memory leak when pre-filling MTLCommandBuffers.

Wrap MVKCommandBuffer::prefill() in autoreleasepool.
For non-reusable MVKCommandBuffers, release command instances
once prefilling is done to reduce memory pressure.
This commit is contained in:
Bill Hollings 2020-03-14 16:56:12 -04:00
parent 30658f05f5
commit 7658bccfba
4 changed files with 26 additions and 17 deletions

View File

@ -17,16 +17,15 @@ For best results, use a Markdown reader.*
MoltenVK 1.0.41
---------------
Released TBD
Released 2020/03/30
- Fix issue where immutable samplers are removed during descriptor update.
- Guard against Metal validation assertion from reuse of query number
within a single `MTLRenderEncoder`.
- Guard against Metal validation assertion from reuse of query number within a single `MTLRenderEncoder`.
- Increase value of `VkPhysicalDeviceLimits::minStorageBufferOffsetAlignment`
to `16` to avoid Metal validation assertions.
- Add ability to disable command memory pooling using `MVK_CONFIG_USE_COMMAND_POOLING`
environment variable.
- Fix memory leak when pre-filling `MTLCommandBuffers` using `MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS`.

View File

@ -152,6 +152,7 @@ protected:
bool canPrefill();
void prefill();
void clearPrefilledMTLCommandBuffer();
void releaseCommands();
MVKCommand* _head = nullptr;
MVKCommand* _tail = nullptr;

View File

@ -54,18 +54,20 @@ VkResult MVKCommandBuffer::begin(const VkCommandBufferBeginInfo* pBeginInfo) {
return getConfigurationResult();
}
VkResult MVKCommandBuffer::reset(VkCommandBufferResetFlags flags) {
void MVKCommandBuffer::releaseCommands() {
MVKCommand* cmd = _head;
while (cmd) {
MVKCommand* nextCmd = cmd->_next; // Establish next before returning current to pool.
cmd->returnToPool();
cmd = nextCmd;
}
clearPrefilledMTLCommandBuffer();
_head = nullptr;
_tail = nullptr;
}
VkResult MVKCommandBuffer::reset(VkCommandBufferResetFlags flags) {
clearPrefilledMTLCommandBuffer();
releaseCommands();
_doesContinueRenderPass = false;
_canAcceptCommands = false;
_isReusable = false;
@ -140,18 +142,24 @@ bool MVKCommandBuffer::canExecute() {
return true;
}
// If we can, prefill a MTLCommandBuffer with the commands in this command buffer
// If we can, prefill a MTLCommandBuffer with the commands in this command buffer.
// Wrap in autorelease pool to capture autoreleased Metal encoding activity.
void MVKCommandBuffer::prefill() {
@autoreleasepool {
clearPrefilledMTLCommandBuffer();
clearPrefilledMTLCommandBuffer();
if ( !canPrefill() ) { return; }
if ( !canPrefill() ) { return; }
uint32_t qIdx = 0;
_prefilledMTLCmdBuffer = _commandPool->newMTLCommandBuffer(qIdx); // retain
uint32_t qIdx = 0;
_prefilledMTLCmdBuffer = _commandPool->newMTLCommandBuffer(qIdx); // retain
MVKCommandEncoder encoder(this);
encoder.encode(_prefilledMTLCmdBuffer);
MVKCommandEncoder encoder(this);
encoder.encode(_prefilledMTLCmdBuffer);
// Once encoded onto Metal, if this command buffer is not reusable, we don't need the
// MVKCommand instances anymore, so release them in order to reduce memory pressure.
if ( !_isReusable ) { releaseCommands(); }
}
}
bool MVKCommandBuffer::canPrefill() {

View File

@ -249,7 +249,7 @@ void MVKQueueCommandBufferSubmission::setActiveMTLCommandBuffer(id<MTLCommandBuf
if (_activeMTLCommandBuffer) { commitActiveMTLCommandBuffer(); }
_activeMTLCommandBuffer = mtlCmdBuff; // not retained
_activeMTLCommandBuffer = [mtlCmdBuff retain]; // retained to handle prefilled
[_activeMTLCommandBuffer enqueue];
}
@ -276,8 +276,9 @@ void MVKQueueCommandBufferSubmission::commitActiveMTLCommandBuffer(bool signalCo
// Use temp var because callback may destroy this instance before this function ends.
id<MTLCommandBuffer> mtlCmdBuff = _activeMTLCommandBuffer;
_activeMTLCommandBuffer = nil; // not retained
_activeMTLCommandBuffer = nil;
[mtlCmdBuff commit];
[mtlCmdBuff release]; // retained
}
void MVKQueueCommandBufferSubmission::finish() {