Add ability to disable command memory pooling using MVK_CONFIG_USE_COMMAND_POOLING

environment variable.

Add ability to track MVKObjectPool allocation and pool residency counts.
This commit is contained in:
Bill Hollings 2020-03-11 17:50:49 -04:00
parent bf4d121897
commit 30658f05f5
7 changed files with 117 additions and 69 deletions

View File

@ -24,6 +24,9 @@ Released TBD
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.

View File

@ -154,6 +154,15 @@ typedef unsigned long MTLLanguageVersion;
* be dynamically allocated in application memory when the descriptor set itself is allocated.
* This setting is disabled by default, and MoltenVK will dynamically allocate descriptors
* when the containing descriptor set is allocated.
*
* 8. The MVK_CONFIG_USE_COMMAND_POOLING runtime environment variable or MoltenVK compile-time
* build setting controls whether MoltenVK should use pools to manage memory used when
* adding commands to command buffers. If this environment variable is enabled, MoltenVK
* will use a pool to hold command resources for reuse during command execution. If this
* environment variable is disabled, command memory is allocated and destroyed each time
* a command is executed. This is a classic time-space trade off. When command pooling is
* active, the memory in the pool can be cleared via a call to the vkTrimCommandPoolKHR()
* command. This setting is enabled by default, and MoltenVK will pool command memory.
*/
typedef struct {

View File

@ -182,7 +182,9 @@ public:
/** Resets the command pool. */
VkResult reset( VkCommandPoolResetFlags flags);
MVKCommandPool(MVKDevice* device, const VkCommandPoolCreateInfo* pCreateInfo);
MVKCommandPool(MVKDevice* device,
const VkCommandPoolCreateInfo* pCreateInfo,
bool usePooling);
~MVKCommandPool() override;

View File

@ -134,57 +134,58 @@ void MVKCommandPool::trim() {
#pragma mark Construction
MVKCommandPool::MVKCommandPool(MVKDevice* device,
const VkCommandPoolCreateInfo* pCreateInfo) :
const VkCommandPoolCreateInfo* pCreateInfo,
bool usePooling) :
MVKVulkanAPIDeviceObject(device),
_queueFamilyIndex(pCreateInfo->queueFamilyIndex),
_commandBufferPool(device),
_commandBufferPool(device, usePooling),
_commandEncodingPool(this),
_cmdPipelineBarrierPool(this),
_cmdBindPipelinePool(this),
_cmdBeginRenderPassPool(this),
_cmdNextSubpassPool(this),
_cmdExecuteCommandsPool(this),
_cmdEndRenderPassPool(this),
_cmdBindDescriptorSetsPool(this),
_cmdSetViewportPool(this),
_cmdSetScissorPool(this),
_cmdSetLineWidthPool(this),
_cmdSetDepthBiasPool(this),
_cmdSetBlendConstantsPool(this),
_cmdSetDepthBoundsPool(this),
_cmdSetStencilCompareMaskPool(this),
_cmdSetStencilWriteMaskPool(this),
_cmdSetStencilReferencePool(this),
_cmdBindVertexBuffersPool(this),
_cmdBindIndexBufferPool(this),
_cmdDrawPool(this),
_cmdDrawIndexedPool(this),
_cmdDrawIndirectPool(this),
_cmdDrawIndexedIndirectPool(this),
_cmdCopyImagePool(this),
_cmdBlitImagePool(this),
_cmdResolveImagePool(this),
_cmdFillBufferPool(this),
_cmdUpdateBufferPool(this),
_cmdCopyBufferPool(this),
_cmdBufferImageCopyPool(this),
_cmdClearAttachmentsPool(this),
_cmdClearImagePool(this),
_cmdBeginQueryPool(this),
_cmdEndQueryPool(this),
_cmdWriteTimestampPool(this),
_cmdResetQueryPoolPool(this),
_cmdCopyQueryPoolResultsPool(this),
_cmdPushConstantsPool(this),
_cmdDispatchPool(this),
_cmdDispatchIndirectPool(this),
_cmdPushDescriptorSetPool(this),
_cmdPushSetWithTemplatePool(this),
_cmdDebugMarkerBeginPool(this),
_cmdDebugMarkerEndPool(this),
_cmdDebugMarkerInsertPool(this),
_cmdSetResetEventPool(this),
_cmdWaitEventsPool(this)
_cmdPipelineBarrierPool(this, usePooling),
_cmdBindPipelinePool(this, usePooling),
_cmdBeginRenderPassPool(this, usePooling),
_cmdNextSubpassPool(this, usePooling),
_cmdExecuteCommandsPool(this, usePooling),
_cmdEndRenderPassPool(this, usePooling),
_cmdBindDescriptorSetsPool(this, usePooling),
_cmdSetViewportPool(this, usePooling),
_cmdSetScissorPool(this, usePooling),
_cmdSetLineWidthPool(this, usePooling),
_cmdSetDepthBiasPool(this, usePooling),
_cmdSetBlendConstantsPool(this, usePooling),
_cmdSetDepthBoundsPool(this, usePooling),
_cmdSetStencilCompareMaskPool(this, usePooling),
_cmdSetStencilWriteMaskPool(this, usePooling),
_cmdSetStencilReferencePool(this, usePooling),
_cmdBindVertexBuffersPool(this, usePooling),
_cmdBindIndexBufferPool(this, usePooling),
_cmdDrawPool(this, usePooling),
_cmdDrawIndexedPool(this, usePooling),
_cmdDrawIndirectPool(this, usePooling),
_cmdDrawIndexedIndirectPool(this, usePooling),
_cmdCopyImagePool(this, usePooling),
_cmdBlitImagePool(this, usePooling),
_cmdResolveImagePool(this, usePooling),
_cmdFillBufferPool(this, usePooling),
_cmdUpdateBufferPool(this, usePooling),
_cmdCopyBufferPool(this, usePooling),
_cmdBufferImageCopyPool(this, usePooling),
_cmdClearAttachmentsPool(this, usePooling),
_cmdClearImagePool(this, usePooling),
_cmdBeginQueryPool(this, usePooling),
_cmdEndQueryPool(this, usePooling),
_cmdWriteTimestampPool(this, usePooling),
_cmdResetQueryPoolPool(this, usePooling),
_cmdCopyQueryPoolResultsPool(this, usePooling),
_cmdPushConstantsPool(this, usePooling),
_cmdDispatchPool(this, usePooling),
_cmdDispatchIndirectPool(this, usePooling),
_cmdPushDescriptorSetPool(this, usePooling),
_cmdPushSetWithTemplatePool(this, usePooling),
_cmdDebugMarkerBeginPool(this, usePooling),
_cmdDebugMarkerEndPool(this, usePooling),
_cmdDebugMarkerInsertPool(this, usePooling),
_cmdSetResetEventPool(this, usePooling),
_cmdWaitEventsPool(this, usePooling)
// when extending be sure to add to trim() as well
{}

View File

@ -666,16 +666,6 @@ public:
/** Performance statistics. */
MVKPerformanceStatistics _performanceStatistics;
// Indicates whether semaphores should use a MTLFence if available.
// Set by the MVK_ALLOW_METAL_FENCES environment variable if MTLFences are available.
// This should be a temporary fix after some repair to semaphore handling.
bool _useMTLFenceForSemaphores;
// Indicates whether semaphores should use a MTLEvent if available.
// Set by the MVK_ALLOW_METAL_EVENTS environment variable if MTLEvents are available.
// This should be a temporary fix after some repair to semaphore handling.
bool _useMTLEventForSemaphores;
#pragma mark Construction
@ -724,6 +714,9 @@ protected:
id<MTLBuffer> _globalVisibilityResultMTLBuffer;
uint32_t _globalVisibilityQueryCount;
std::mutex _vizLock;
bool _useMTLFenceForSemaphores;
bool _useMTLEventForSemaphores;
bool _useCommandPooling;
};

View File

@ -2395,7 +2395,7 @@ void MVKDevice::destroyRenderPass(MVKRenderPass* mvkRP,
MVKCommandPool* MVKDevice::createCommandPool(const VkCommandPoolCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator) {
return new MVKCommandPool(this, pCreateInfo);
return new MVKCommandPool(this, pCreateInfo, _useCommandPooling);
}
void MVKDevice::destroyCommandPool(MVKCommandPool* mvkCmdPool,
@ -2650,16 +2650,30 @@ void MVKDevice::initPhysicalDevice(MVKPhysicalDevice* physicalDevice, const VkDe
_pProperties = &_physicalDevice->_properties;
_pMemoryProperties = &_physicalDevice->_memoryProperties;
// Indicates whether semaphores should use a MTLFence if available.
// Set by the MVK_ALLOW_METAL_FENCES environment variable if MTLFences are available.
// This should be a temporary fix after some repair to semaphore handling.
_useMTLFenceForSemaphores = false;
if (_pMetalFeatures->fences) {
MVK_SET_FROM_ENV_OR_BUILD_BOOL(_useMTLFenceForSemaphores, MVK_ALLOW_METAL_FENCES);
}
// Indicates whether semaphores should use a MTLEvent if available.
// Set by the MVK_ALLOW_METAL_EVENTS environment variable if MTLEvents are available.
// This should be a temporary fix after some repair to semaphore handling.
_useMTLEventForSemaphores = false;
if (_pMetalFeatures->events) {
MVK_SET_FROM_ENV_OR_BUILD_BOOL(_useMTLEventForSemaphores, MVK_ALLOW_METAL_EVENTS);
}
MVKLogInfo("Using %s for Vulkan semaphores.", _useMTLFenceForSemaphores ? "MTLFence" : (_useMTLEventForSemaphores ? "MTLEvent" : "emulation"));
#ifndef MVK_CONFIG_USE_COMMAND_POOLING
# define MVK_CONFIG_USE_COMMAND_POOLING 1
#endif
_useCommandPooling = MVK_CONFIG_USE_COMMAND_POOLING;
MVK_SET_FROM_ENV_OR_BUILD_BOOL(_useCommandPooling, MVK_CONFIG_USE_COMMAND_POOLING);
#if MVK_MACOS
// If we have selected a high-power GPU and want to force the window system
// to use it, force the window system to use a high-power GPU by calling the

View File

@ -23,6 +23,9 @@
#include <mutex>
#pragma mark -
#pragma mark MVKLinkableMixin
/**
* Instances of sublcasses of this mixin can participate in a typed linked list or pool.
* A simple implementation of the CRTP (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).
@ -47,6 +50,13 @@ protected:
#pragma mark -
#pragma mark MVKObjectPool
/** Track pool stats. */
typedef struct {
uint64_t created = 0;
uint64_t alive = 0;
uint64_t resident = 0;
} MVKObjectPoolCounts;
/**
* Manages a pool of instances of a particular object type.
*
@ -80,9 +90,13 @@ public:
* aquireObject() and returnObject() must be made from the same thread.
*/
T* acquireObject() {
T* obj = VK_NULL_HANDLE;
T* obj = nullptr;
if (_isPooling) { obj = nextObject(); }
if ( !obj ) { obj = newObject(); }
if ( !obj ) {
obj = newObject();
_counts.created++;
_counts.alive++;
}
return obj;
}
@ -102,11 +116,12 @@ public:
if (_isPooling) {
if (_tail) { _tail->_next = obj; }
obj->_next = VK_NULL_HANDLE;
obj->_next = nullptr;
_tail = obj;
if ( !_head ) { _head = obj; }
_counts.resident++;
} else {
obj->destroy();
destroyObject(obj);
}
}
@ -122,12 +137,15 @@ public:
returnObject(obj);
}
/** Clears all the objects from this pool, deleting each one. This method is thread-safe. */
/** Clears all the objects from this pool, destroying each one. This method is thread-safe. */
void clear() {
std::lock_guard<std::mutex> lock(_lock);
while ( T* obj = nextObject() ) { obj->destroy(); }
while ( T* obj = nextObject() ) { destroyObject(obj); }
}
/** Returns the current counts. */
MVKObjectPoolCounts getCounts() { return _counts; }
/**
* Configures this instance to either use pooling, or not, depending on the
* value of isPooling, which defaults to true if not indicated explicitly.
@ -146,9 +164,10 @@ protected:
T* nextObject() {
T* obj = _head;
if (obj) {
_head = (T*)obj->_next; // Will be null for last object in pool
if ( !_head ) { _tail = VK_NULL_HANDLE; } // If last, also clear tail
obj->_next = VK_NULL_HANDLE; // Objects in the wild should never think they are still part of this pool
_head = (T*)obj->_next; // Will be null for last object in pool
if ( !_head ) { _tail = nullptr; } // If last, also clear tail
obj->_next = nullptr; // Objects in the wild should never think they are still part of this pool
_counts.resident--;
}
return obj;
}
@ -156,9 +175,16 @@ protected:
/** Returns a new instance of the type of object managed by this pool. */
virtual T* newObject() = 0;
/** Destroys the object. */
void destroyObject(T* obj) {
obj->destroy();
_counts.alive--;
}
std::mutex _lock;
T* _head = nullptr;
T* _tail = nullptr;
bool _isPooling;
MVKObjectPoolCounts _counts;
};