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`.
|
within a single `MTLRenderEncoder`.
|
||||||
- Increase value of `VkPhysicalDeviceLimits::minStorageBufferOffsetAlignment`
|
- Increase value of `VkPhysicalDeviceLimits::minStorageBufferOffsetAlignment`
|
||||||
to `16` to avoid Metal validation assertions.
|
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.
|
* 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
|
* This setting is disabled by default, and MoltenVK will dynamically allocate descriptors
|
||||||
* when the containing descriptor set is allocated.
|
* 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 {
|
typedef struct {
|
||||||
|
|
||||||
|
@ -182,7 +182,9 @@ public:
|
|||||||
/** Resets the command pool. */
|
/** Resets the command pool. */
|
||||||
VkResult reset( VkCommandPoolResetFlags flags);
|
VkResult reset( VkCommandPoolResetFlags flags);
|
||||||
|
|
||||||
MVKCommandPool(MVKDevice* device, const VkCommandPoolCreateInfo* pCreateInfo);
|
MVKCommandPool(MVKDevice* device,
|
||||||
|
const VkCommandPoolCreateInfo* pCreateInfo,
|
||||||
|
bool usePooling);
|
||||||
|
|
||||||
~MVKCommandPool() override;
|
~MVKCommandPool() override;
|
||||||
|
|
||||||
|
@ -134,57 +134,58 @@ void MVKCommandPool::trim() {
|
|||||||
#pragma mark Construction
|
#pragma mark Construction
|
||||||
|
|
||||||
MVKCommandPool::MVKCommandPool(MVKDevice* device,
|
MVKCommandPool::MVKCommandPool(MVKDevice* device,
|
||||||
const VkCommandPoolCreateInfo* pCreateInfo) :
|
const VkCommandPoolCreateInfo* pCreateInfo,
|
||||||
|
bool usePooling) :
|
||||||
MVKVulkanAPIDeviceObject(device),
|
MVKVulkanAPIDeviceObject(device),
|
||||||
_queueFamilyIndex(pCreateInfo->queueFamilyIndex),
|
_queueFamilyIndex(pCreateInfo->queueFamilyIndex),
|
||||||
_commandBufferPool(device),
|
_commandBufferPool(device, usePooling),
|
||||||
_commandEncodingPool(this),
|
_commandEncodingPool(this),
|
||||||
_cmdPipelineBarrierPool(this),
|
_cmdPipelineBarrierPool(this, usePooling),
|
||||||
_cmdBindPipelinePool(this),
|
_cmdBindPipelinePool(this, usePooling),
|
||||||
_cmdBeginRenderPassPool(this),
|
_cmdBeginRenderPassPool(this, usePooling),
|
||||||
_cmdNextSubpassPool(this),
|
_cmdNextSubpassPool(this, usePooling),
|
||||||
_cmdExecuteCommandsPool(this),
|
_cmdExecuteCommandsPool(this, usePooling),
|
||||||
_cmdEndRenderPassPool(this),
|
_cmdEndRenderPassPool(this, usePooling),
|
||||||
_cmdBindDescriptorSetsPool(this),
|
_cmdBindDescriptorSetsPool(this, usePooling),
|
||||||
_cmdSetViewportPool(this),
|
_cmdSetViewportPool(this, usePooling),
|
||||||
_cmdSetScissorPool(this),
|
_cmdSetScissorPool(this, usePooling),
|
||||||
_cmdSetLineWidthPool(this),
|
_cmdSetLineWidthPool(this, usePooling),
|
||||||
_cmdSetDepthBiasPool(this),
|
_cmdSetDepthBiasPool(this, usePooling),
|
||||||
_cmdSetBlendConstantsPool(this),
|
_cmdSetBlendConstantsPool(this, usePooling),
|
||||||
_cmdSetDepthBoundsPool(this),
|
_cmdSetDepthBoundsPool(this, usePooling),
|
||||||
_cmdSetStencilCompareMaskPool(this),
|
_cmdSetStencilCompareMaskPool(this, usePooling),
|
||||||
_cmdSetStencilWriteMaskPool(this),
|
_cmdSetStencilWriteMaskPool(this, usePooling),
|
||||||
_cmdSetStencilReferencePool(this),
|
_cmdSetStencilReferencePool(this, usePooling),
|
||||||
_cmdBindVertexBuffersPool(this),
|
_cmdBindVertexBuffersPool(this, usePooling),
|
||||||
_cmdBindIndexBufferPool(this),
|
_cmdBindIndexBufferPool(this, usePooling),
|
||||||
_cmdDrawPool(this),
|
_cmdDrawPool(this, usePooling),
|
||||||
_cmdDrawIndexedPool(this),
|
_cmdDrawIndexedPool(this, usePooling),
|
||||||
_cmdDrawIndirectPool(this),
|
_cmdDrawIndirectPool(this, usePooling),
|
||||||
_cmdDrawIndexedIndirectPool(this),
|
_cmdDrawIndexedIndirectPool(this, usePooling),
|
||||||
_cmdCopyImagePool(this),
|
_cmdCopyImagePool(this, usePooling),
|
||||||
_cmdBlitImagePool(this),
|
_cmdBlitImagePool(this, usePooling),
|
||||||
_cmdResolveImagePool(this),
|
_cmdResolveImagePool(this, usePooling),
|
||||||
_cmdFillBufferPool(this),
|
_cmdFillBufferPool(this, usePooling),
|
||||||
_cmdUpdateBufferPool(this),
|
_cmdUpdateBufferPool(this, usePooling),
|
||||||
_cmdCopyBufferPool(this),
|
_cmdCopyBufferPool(this, usePooling),
|
||||||
_cmdBufferImageCopyPool(this),
|
_cmdBufferImageCopyPool(this, usePooling),
|
||||||
_cmdClearAttachmentsPool(this),
|
_cmdClearAttachmentsPool(this, usePooling),
|
||||||
_cmdClearImagePool(this),
|
_cmdClearImagePool(this, usePooling),
|
||||||
_cmdBeginQueryPool(this),
|
_cmdBeginQueryPool(this, usePooling),
|
||||||
_cmdEndQueryPool(this),
|
_cmdEndQueryPool(this, usePooling),
|
||||||
_cmdWriteTimestampPool(this),
|
_cmdWriteTimestampPool(this, usePooling),
|
||||||
_cmdResetQueryPoolPool(this),
|
_cmdResetQueryPoolPool(this, usePooling),
|
||||||
_cmdCopyQueryPoolResultsPool(this),
|
_cmdCopyQueryPoolResultsPool(this, usePooling),
|
||||||
_cmdPushConstantsPool(this),
|
_cmdPushConstantsPool(this, usePooling),
|
||||||
_cmdDispatchPool(this),
|
_cmdDispatchPool(this, usePooling),
|
||||||
_cmdDispatchIndirectPool(this),
|
_cmdDispatchIndirectPool(this, usePooling),
|
||||||
_cmdPushDescriptorSetPool(this),
|
_cmdPushDescriptorSetPool(this, usePooling),
|
||||||
_cmdPushSetWithTemplatePool(this),
|
_cmdPushSetWithTemplatePool(this, usePooling),
|
||||||
_cmdDebugMarkerBeginPool(this),
|
_cmdDebugMarkerBeginPool(this, usePooling),
|
||||||
_cmdDebugMarkerEndPool(this),
|
_cmdDebugMarkerEndPool(this, usePooling),
|
||||||
_cmdDebugMarkerInsertPool(this),
|
_cmdDebugMarkerInsertPool(this, usePooling),
|
||||||
_cmdSetResetEventPool(this),
|
_cmdSetResetEventPool(this, usePooling),
|
||||||
_cmdWaitEventsPool(this)
|
_cmdWaitEventsPool(this, usePooling)
|
||||||
// when extending be sure to add to trim() as well
|
// when extending be sure to add to trim() as well
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -666,16 +666,6 @@ public:
|
|||||||
/** Performance statistics. */
|
/** Performance statistics. */
|
||||||
MVKPerformanceStatistics _performanceStatistics;
|
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
|
#pragma mark Construction
|
||||||
|
|
||||||
@ -724,6 +714,9 @@ protected:
|
|||||||
id<MTLBuffer> _globalVisibilityResultMTLBuffer;
|
id<MTLBuffer> _globalVisibilityResultMTLBuffer;
|
||||||
uint32_t _globalVisibilityQueryCount;
|
uint32_t _globalVisibilityQueryCount;
|
||||||
std::mutex _vizLock;
|
std::mutex _vizLock;
|
||||||
|
bool _useMTLFenceForSemaphores;
|
||||||
|
bool _useMTLEventForSemaphores;
|
||||||
|
bool _useCommandPooling;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -2395,7 +2395,7 @@ void MVKDevice::destroyRenderPass(MVKRenderPass* mvkRP,
|
|||||||
|
|
||||||
MVKCommandPool* MVKDevice::createCommandPool(const VkCommandPoolCreateInfo* pCreateInfo,
|
MVKCommandPool* MVKDevice::createCommandPool(const VkCommandPoolCreateInfo* pCreateInfo,
|
||||||
const VkAllocationCallbacks* pAllocator) {
|
const VkAllocationCallbacks* pAllocator) {
|
||||||
return new MVKCommandPool(this, pCreateInfo);
|
return new MVKCommandPool(this, pCreateInfo, _useCommandPooling);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MVKDevice::destroyCommandPool(MVKCommandPool* mvkCmdPool,
|
void MVKDevice::destroyCommandPool(MVKCommandPool* mvkCmdPool,
|
||||||
@ -2650,16 +2650,30 @@ void MVKDevice::initPhysicalDevice(MVKPhysicalDevice* physicalDevice, const VkDe
|
|||||||
_pProperties = &_physicalDevice->_properties;
|
_pProperties = &_physicalDevice->_properties;
|
||||||
_pMemoryProperties = &_physicalDevice->_memoryProperties;
|
_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;
|
_useMTLFenceForSemaphores = false;
|
||||||
if (_pMetalFeatures->fences) {
|
if (_pMetalFeatures->fences) {
|
||||||
MVK_SET_FROM_ENV_OR_BUILD_BOOL(_useMTLFenceForSemaphores, MVK_ALLOW_METAL_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;
|
_useMTLEventForSemaphores = false;
|
||||||
if (_pMetalFeatures->events) {
|
if (_pMetalFeatures->events) {
|
||||||
MVK_SET_FROM_ENV_OR_BUILD_BOOL(_useMTLEventForSemaphores, MVK_ALLOW_METAL_EVENTS);
|
MVK_SET_FROM_ENV_OR_BUILD_BOOL(_useMTLEventForSemaphores, MVK_ALLOW_METAL_EVENTS);
|
||||||
}
|
}
|
||||||
MVKLogInfo("Using %s for Vulkan semaphores.", _useMTLFenceForSemaphores ? "MTLFence" : (_useMTLEventForSemaphores ? "MTLEvent" : "emulation"));
|
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 MVK_MACOS
|
||||||
// If we have selected a high-power GPU and want to force the window system
|
// 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
|
// to use it, force the window system to use a high-power GPU by calling the
|
||||||
|
@ -23,6 +23,9 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark MVKLinkableMixin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instances of sublcasses of this mixin can participate in a typed linked list or pool.
|
* 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).
|
* A simple implementation of the CRTP (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).
|
||||||
@ -47,6 +50,13 @@ protected:
|
|||||||
#pragma mark -
|
#pragma mark -
|
||||||
#pragma mark MVKObjectPool
|
#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.
|
* 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.
|
* aquireObject() and returnObject() must be made from the same thread.
|
||||||
*/
|
*/
|
||||||
T* acquireObject() {
|
T* acquireObject() {
|
||||||
T* obj = VK_NULL_HANDLE;
|
T* obj = nullptr;
|
||||||
if (_isPooling) { obj = nextObject(); }
|
if (_isPooling) { obj = nextObject(); }
|
||||||
if ( !obj ) { obj = newObject(); }
|
if ( !obj ) {
|
||||||
|
obj = newObject();
|
||||||
|
_counts.created++;
|
||||||
|
_counts.alive++;
|
||||||
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@ -102,11 +116,12 @@ public:
|
|||||||
|
|
||||||
if (_isPooling) {
|
if (_isPooling) {
|
||||||
if (_tail) { _tail->_next = obj; }
|
if (_tail) { _tail->_next = obj; }
|
||||||
obj->_next = VK_NULL_HANDLE;
|
obj->_next = nullptr;
|
||||||
_tail = obj;
|
_tail = obj;
|
||||||
if ( !_head ) { _head = obj; }
|
if ( !_head ) { _head = obj; }
|
||||||
|
_counts.resident++;
|
||||||
} else {
|
} else {
|
||||||
obj->destroy();
|
destroyObject(obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,12 +137,15 @@ public:
|
|||||||
returnObject(obj);
|
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() {
|
void clear() {
|
||||||
std::lock_guard<std::mutex> lock(_lock);
|
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
|
* Configures this instance to either use pooling, or not, depending on the
|
||||||
* value of isPooling, which defaults to true if not indicated explicitly.
|
* value of isPooling, which defaults to true if not indicated explicitly.
|
||||||
@ -146,9 +164,10 @@ protected:
|
|||||||
T* nextObject() {
|
T* nextObject() {
|
||||||
T* obj = _head;
|
T* obj = _head;
|
||||||
if (obj) {
|
if (obj) {
|
||||||
_head = (T*)obj->_next; // Will be null for last object in pool
|
_head = (T*)obj->_next; // Will be null for last object in pool
|
||||||
if ( !_head ) { _tail = VK_NULL_HANDLE; } // If last, also clear tail
|
if ( !_head ) { _tail = nullptr; } // If last, also clear tail
|
||||||
obj->_next = VK_NULL_HANDLE; // Objects in the wild should never think they are still part of this pool
|
obj->_next = nullptr; // Objects in the wild should never think they are still part of this pool
|
||||||
|
_counts.resident--;
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@ -156,9 +175,16 @@ protected:
|
|||||||
/** Returns a new instance of the type of object managed by this pool. */
|
/** Returns a new instance of the type of object managed by this pool. */
|
||||||
virtual T* newObject() = 0;
|
virtual T* newObject() = 0;
|
||||||
|
|
||||||
|
/** Destroys the object. */
|
||||||
|
void destroyObject(T* obj) {
|
||||||
|
obj->destroy();
|
||||||
|
_counts.alive--;
|
||||||
|
}
|
||||||
|
|
||||||
std::mutex _lock;
|
std::mutex _lock;
|
||||||
T* _head = nullptr;
|
T* _head = nullptr;
|
||||||
T* _tail = nullptr;
|
T* _tail = nullptr;
|
||||||
bool _isPooling;
|
bool _isPooling;
|
||||||
|
MVKObjectPoolCounts _counts;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user