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:
parent
bf4d121897
commit
30658f05f5
@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
{}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user