Skip to content

Commit

Permalink
Merge pull request #173 from billhollings/master
Browse files Browse the repository at this point in the history
Device memory management changes.
  • Loading branch information
billhollings authored Jun 5, 2018
2 parents 8aa29c4 + c2324a5 commit 8d9ee9b
Show file tree
Hide file tree
Showing 10 changed files with 349 additions and 450 deletions.
4 changes: 2 additions & 2 deletions MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ extern "C" {
*/
#define MVK_VERSION_MAJOR 1
#define MVK_VERSION_MINOR 0
#define MVK_VERSION_PATCH 9
#define MVK_VERSION_PATCH 10

#define MVK_MAKE_VERSION(major, minor, patch) (((major) * 10000) + ((minor) * 100) + (patch))
#define MVK_VERSION MVK_MAKE_VERSION(MVK_VERSION_MAJOR, MVK_VERSION_MINOR, MVK_VERSION_PATCH)


#define VK_MVK_MOLTENVK_SPEC_VERSION 4
#define VK_MVK_MOLTENVK_SPEC_VERSION 5
#define VK_MVK_MOLTENVK_EXTENSION_NAME "VK_MVK_moltenvk"

/**
Expand Down
15 changes: 5 additions & 10 deletions MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class MVKBuffer : public MVKResource {
/** Returns the memory requirements of this resource by populating the specified structure. */
VkResult getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) override;

/** Binds this resource to the specified offset within the specified memory allocation. */
VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) override;

/** Applies the specified global memory barrier. */
void applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
Expand All @@ -54,10 +57,10 @@ class MVKBuffer : public MVKResource {
#pragma mark Metal

/** Returns the Metal buffer underlying this memory allocation. */
id<MTLBuffer> getMTLBuffer();
inline id<MTLBuffer> getMTLBuffer() { return _deviceMemory ? _deviceMemory->getMTLBuffer() : nullptr; }

/** Returns the offset at which the contents of this instance starts within the underlying Metal buffer. */
NSUInteger getMTLBufferOffset();
inline NSUInteger getMTLBufferOffset() { return _deviceMemoryOffset; }


#pragma mark Construction
Expand All @@ -69,17 +72,9 @@ class MVKBuffer : public MVKResource {
protected:
using MVKResource::needsHostReadSync;

void* map(VkDeviceSize offset, VkDeviceSize size) override;
VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size) override;
VkResult pullFromDevice(VkDeviceSize offset, VkDeviceSize size) override;
VkResult copyMTLBufferContent(VkDeviceSize offset, VkDeviceSize size, bool intoMTLBuffer);
NSRange mtlBufferRange(VkDeviceSize offset, VkDeviceSize size);
bool needsHostReadSync(VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
VkBufferMemoryBarrier* pBufferMemoryBarrier);

id<MTLBuffer> _mtlBuffer;
std::mutex _lock;
};


Expand Down
112 changes: 11 additions & 101 deletions MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,19 @@

VkResult MVKBuffer::getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) {
pMemoryRequirements->size = getByteCount();
pMemoryRequirements->alignment = getByteAlignment();
pMemoryRequirements->alignment = _byteAlignment;
pMemoryRequirements->memoryTypeBits = _device->getPhysicalDevice()->getAllMemoryTypes();
return VK_SUCCESS;
}

VkResult MVKBuffer::bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) {
if (_deviceMemory) { _deviceMemory->removeBuffer(this); }

MVKResource::bindDeviceMemory(mvkMem, memOffset);

return _deviceMemory ? _deviceMemory->addBuffer(this) : VK_SUCCESS;
}

void MVKBuffer::applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
VkMemoryBarrier* pMemoryBarrier,
Expand Down Expand Up @@ -73,104 +81,8 @@
#if MVK_MACOS
return (mvkIsAnyFlagEnabled(dstStageMask, (VK_PIPELINE_STAGE_HOST_BIT)) &&
mvkIsAnyFlagEnabled(pBufferMemoryBarrier->dstAccessMask, (VK_ACCESS_HOST_READ_BIT)) &&
_deviceMemory->isMemoryHostAccessible() && !_deviceMemory->isMemoryHostCoherent());
#endif
}

/** Called when the bound device memory is updated. Flushes any associated resource memory. */
VkResult MVKBuffer::flushToDevice(VkDeviceSize offset, VkDeviceSize size) {
VkResult rslt = copyMTLBufferContent(offset, size, true);

#if MVK_MACOS
if (_deviceMemory->getMTLStorageMode() == MTLStorageModeManaged) {
[getMTLBuffer() didModifyRange: mtlBufferRange(offset, size)];
}
isMemoryHostAccessible() && !isMemoryHostCoherent());
#endif

return rslt;
}

// Called when the bound device memory is invalidated. Pulls any associated resource memory from the device.
VkResult MVKBuffer::pullFromDevice(VkDeviceSize offset, VkDeviceSize size) {
VkResult rslt = copyMTLBufferContent(offset, size, false);

// If we are pulling to populate a newly created device memory MTLBuffer,
// from a previously created local MTLBuffer, remove the local MTLBuffer.
// Use autorelease in case the earlier MTLBuffer was encoded.
if (_mtlBuffer && _deviceMemory->getMTLBuffer()) {
[_mtlBuffer autorelease];
_mtlBuffer = nil;
}

return rslt;
}

void* MVKBuffer::map(VkDeviceSize offset, VkDeviceSize size) {
return (void*)((uintptr_t)getMTLBuffer().contents + mtlBufferRange(offset, size).location);
}

// Copies host content into or out of the MTLBuffer.
VkResult MVKBuffer::copyMTLBufferContent(VkDeviceSize offset, VkDeviceSize size, bool intoMTLBuffer) {

// Only copy if there is separate host memory and this buffer overlaps the host memory range
void* pMemBase = _deviceMemory->getLogicalMappedMemory();
if (pMemBase && doesOverlap(offset, size)) {

NSRange copyRange = mtlBufferRange(offset, size);
VkDeviceSize memOffset = max(offset, _deviceMemoryOffset);

// MVKLogDebug("Copying contents %s buffer %p at buffer offset %d memory offset %d and length %d.", (intoMTLBuffer ? "to" : "from"), this, copyRange.location, memOffset, copyRange.length);

void* pMemBytes = (void*)((uintptr_t)pMemBase + memOffset);
void* pMTLBuffBytes = (void*)((uintptr_t)getMTLBuffer().contents + copyRange.location);

// Copy in the direction indicated.
// Don't copy if the source and destination are the same address, which will
// occur if the underlying MTLBuffer comes from the device memory object.
if (pMemBytes != pMTLBuffBytes) {
// MVKLogDebug("Copying buffer contents.");
if (intoMTLBuffer) {
memcpy(pMTLBuffBytes, pMemBytes, copyRange.length);
} else {
memcpy(pMemBytes, pMTLBuffBytes, copyRange.length);
}
}
}

return VK_SUCCESS;
}


#pragma mark Metal

// If a local MTLBuffer already exists, use it.
// If the device memory has a MTLBuffer, use it.
// Otherwise, create a new MTLBuffer and use it from now on.
id<MTLBuffer> MVKBuffer::getMTLBuffer() {

if (_mtlBuffer) { return _mtlBuffer; }

id<MTLBuffer> devMemMTLBuff = _deviceMemory->getMTLBuffer();
if (devMemMTLBuff) { return devMemMTLBuff; }

// Lock and check again in case another thread has created the buffer.
lock_guard<mutex> lock(_lock);
if (_mtlBuffer) { return _mtlBuffer; }

NSUInteger mtlBuffLen = mvkAlignByteOffset(_byteCount, _byteAlignment);
_mtlBuffer = [getMTLDevice() newBufferWithLength: mtlBuffLen
options: _deviceMemory->getMTLResourceOptions()]; // retained
// MVKLogDebug("MVKBuffer %p creating local MTLBuffer of size %d.", this, _mtlBuffer.length);
return _mtlBuffer;
}

NSUInteger MVKBuffer::getMTLBufferOffset() { return _mtlBuffer ? 0 : _deviceMemoryOffset; }

// Returns an NSRange that maps the specified host memory range to the MTLBuffer.
NSRange MVKBuffer::mtlBufferRange(VkDeviceSize offset, VkDeviceSize size) {
NSUInteger localRangeLoc = min((offset > _deviceMemoryOffset) ? (offset - _deviceMemoryOffset) : 0, _byteCount);
NSUInteger localRangeLen = min(size, _byteCount - localRangeLoc);
return NSMakeRange(getMTLBufferOffset() + localRangeLoc, localRangeLen);
}


Expand All @@ -179,12 +91,10 @@
MVKBuffer::MVKBuffer(MVKDevice* device, const VkBufferCreateInfo* pCreateInfo) : MVKResource(device) {
_byteAlignment = _device->_pMetalFeatures->mtlBufferAlignment;
_byteCount = pCreateInfo->size;
_mtlBuffer = nil;
}

MVKBuffer::~MVKBuffer() {
[_mtlBuffer release];
_mtlBuffer = nil;
if (_deviceMemory) { _deviceMemory->removeBuffer(this); }
}


Expand Down
56 changes: 26 additions & 30 deletions MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@

#import <Metal/Metal.h>

class MVKResource;
class MVKBuffer;
class MVKImage;


#pragma mark MVKDeviceMemory
Expand All @@ -42,18 +43,11 @@ class MVKDeviceMemory : public MVKBaseDeviceObject {
/** Returns the memory already committed by this instance. */
inline VkDeviceSize getDeviceMemoryCommitment() { return _allocationSize; }

/**
* Returns the host memory address that represents what would be the beginning of the
* mapped address space if the entire device memory represented by this object were to
* be mapped to host memory.
*
* This is the address to which the offset value in the vMapMemory() call references.
* It only has physical meaning if offset is zero, otherwise it is a logical address
* used to calculate resource offsets.
*
* This function must only be called between vkMapMemory() and vkUnmapMemory() calls.
*/
inline void* getLogicalMappedMemory() { return _pLogicalMappedMemory; }
/**
* Returns the host memory address of this memory, or NULL if the memory
* is marked as device-only and cannot be mapped to a host address.
*/
inline void* getHostMemoryAddress() { return _pMemory; }

/**
* Maps the memory address at the specified offset from the start of this memory allocation,
Expand All @@ -64,9 +58,6 @@ class MVKDeviceMemory : public MVKBaseDeviceObject {
/** Unmaps a previously mapped memory range. */
void unmap();

/** Allocates mapped host memory, and returns a pointer to it. */
void* allocateMappedMemory(VkDeviceSize offset, VkDeviceSize size);

/**
* If this memory is host-visible, the specified memory range is flushed to the device.
* Normally, flushing will only occur if the device memory is non-coherent, but flushing
Expand Down Expand Up @@ -107,25 +98,30 @@ class MVKDeviceMemory : public MVKBaseDeviceObject {
~MVKDeviceMemory() override;

protected:
friend MVKResource;
friend MVKBuffer;
friend MVKImage;

VkDeviceSize adjustMemorySize(VkDeviceSize size, VkDeviceSize offset);
bool mapToUniqueResource(VkDeviceSize offset, VkDeviceSize size);
void addResource(MVKResource* rez);
void removeResource(MVKResource* rez);

std::vector<MVKResource*> _resources;
VkResult addBuffer(MVKBuffer* mvkBuff);
void removeBuffer(MVKBuffer* mvkBuff);
VkResult addImage(MVKImage* mvkImg);
void removeImage(MVKImage* mvkImg);
bool ensureMTLBuffer();
bool ensureHostMemory();
void freeHostMemory();

std::vector<MVKBuffer*> _buffers;
std::vector<MVKImage*> _images;
std::mutex _rezLock;
VkDeviceSize _allocationSize;
VkDeviceSize _mapOffset;
VkDeviceSize _mapSize;
id<MTLBuffer> _mtlBuffer;
std::mutex _lock;
VkDeviceSize _allocationSize = 0;
VkDeviceSize _mapOffset = 0;
VkDeviceSize _mapSize = 0;
id<MTLBuffer> _mtlBuffer = nil;
void* _pMemory = nullptr;
void* _pHostMemory = nullptr;
bool _isMapped = false;
MTLResourceOptions _mtlResourceOptions;
MTLStorageMode _mtlStorageMode;
MTLCPUCacheMode _mtlCPUCacheMode;
void* _pMappedHostAllocation;
void* _pMappedMemory;
void* _pLogicalMappedMemory;
};

Loading

0 comments on commit 8d9ee9b

Please sign in to comment.