You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/CrySystem/PageMappingHeap.cpp

259 lines
7.0 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "PageMappingHeap.h"
namespace
{
template <typename Func>
inline void FindZeroRanges(const uint32* str, size_t strLen, Func& yield)
{
size_t carry = 0;
size_t bitIdx = 0;
for (size_t wordIdx = 0; wordIdx < strLen; ++wordIdx)
{
size_t wordBitIdx = 0;
int64 word = str[wordIdx];
// Set up sign extension to insert bits that are the last bit, inverted.
if (!(word & 0x80000000))
{
reinterpret_cast<uint64&>(word) |= 0xffffffff00000000ULL;
}
do
{
size_t wordZeroRunLen = countTrailingZeroes(word);
wordBitIdx += wordZeroRunLen;
carry += wordZeroRunLen;
if (wordBitIdx == 32)
{
break;
}
yield(bitIdx, carry);
word >>= wordZeroRunLen;
bitIdx += carry;
carry = 0;
size_t wordOneRunLen = countTrailingZeroes(~word);
bitIdx += wordOneRunLen;
wordBitIdx += wordOneRunLen;
if (wordBitIdx == 32)
{
break;
}
word >>= wordOneRunLen;
}
while (true);
}
if (carry)
{
yield(bitIdx, carry);
}
}
struct DLMMapFindBest
{
DLMMapFindBest(size_t size)
: requiredLength(size)
, bestPosition(-1)
, bestFragmentLength(INT_MAX)
{
}
bool operator () (size_t position, size_t length)
{
if (length == requiredLength)
{
bestPosition = position;
bestFragmentLength = 0;
return false;
}
else if (length > requiredLength)
{
size_t fragment = length - requiredLength;
if (fragment < bestFragmentLength)
{
bestPosition = position;
bestFragmentLength = fragment;
}
}
return true;
}
size_t requiredLength;
ptrdiff_t bestPosition;
size_t bestFragmentLength;
};
struct FindLargest
{
FindLargest()
: largest(0)
{
}
bool operator () (size_t, size_t length)
{
largest = max(largest, length);
return true;
}
size_t largest;
};
}
CPageMappingHeap::CPageMappingHeap(char* pAddressSpace, size_t nNumPages, size_t nPageSize, const char* sName)
: m_addrRange(pAddressSpace, nPageSize, nNumPages, sName)
{
Init();
}
CPageMappingHeap::CPageMappingHeap(size_t addressSpace, const char* sName)
: m_addrRange(addressSpace, sName)
{
Init();
}
CPageMappingHeap::~CPageMappingHeap()
{
}
void CPageMappingHeap::Release()
{
delete this;
}
size_t CPageMappingHeap::GetGranularity() const
{
return m_addrRange.GetPageSize();
}
bool CPageMappingHeap::IsInAddressRange(void* ptr) const
{
return m_addrRange.IsInRange(ptr);
}
size_t CPageMappingHeap::FindLargestFreeBlockSize() const
{
CryAutoLock<CryCriticalSectionNonRecursive> lock(m_lock);
const size_t pageSize = m_addrRange.GetPageSize();
FindLargest findLargest;
FindZeroRanges(&m_pageBitmap[0], m_pageBitmap.size(), findLargest);
return findLargest.largest * pageSize;
}
void* CPageMappingHeap::Map(size_t length)
{
CryAutoLock<CryCriticalSectionNonRecursive> lock(m_lock);
const size_t pageBitmapElemBitSize = (sizeof(uint32) * 8);
const size_t pageSize = m_addrRange.GetPageSize();
const size_t numPages = m_addrRange.GetPageCount();
if (length % pageSize)
{
__debugbreak();
length = (length + (pageSize - 1)) & ~(pageSize - 1);
}
DLMMapFindBest findBest(length / pageSize);
FindZeroRanges(&m_pageBitmap[0], m_pageBitmap.size(), findBest);
if ((findBest.bestPosition == -1) || (findBest.bestPosition >= (int)numPages))
{
return NULL;
}
void* mapAddress = m_addrRange.GetBaseAddress() + pageSize * findBest.bestPosition;
for (size_t pageIdx = findBest.bestPosition, pageIdxEnd = pageIdx + length / pageSize; pageIdx != pageIdxEnd; ++pageIdx)
{
if (!m_addrRange.MapPage(pageIdx))
{
// Unwind the pages we've already mapped.
for (; pageIdx > static_cast<size_t>(findBest.bestPosition); --pageIdx)
{
m_addrRange.UnmapPage(pageIdx - 1);
}
return NULL;
}
}
for (size_t pageIdx = findBest.bestPosition, pageIdxEnd = pageIdx + length / pageSize; pageIdx != pageIdxEnd; ++pageIdx)
{
size_t pageSegment = pageIdx / pageBitmapElemBitSize;
uint32 pageMask = 1U << static_cast<uint32>(pageIdx % pageBitmapElemBitSize);
m_pageBitmap[pageSegment] |= pageMask;
}
return mapAddress;
}
void CPageMappingHeap::Unmap(void* mem, size_t length)
{
CryAutoLock<CryCriticalSectionNonRecursive> lock(m_lock);
const size_t pageSize = m_addrRange.GetPageSize();
if (length % pageSize)
{
__debugbreak();
length = (length + (pageSize - 1)) & ~(pageSize - 1);
}
char* mapAddress = reinterpret_cast<char*>(mem);
for (size_t pageIdx = (mapAddress - m_addrRange.GetBaseAddress()) / pageSize, pageIdxEnd = pageIdx + length / pageSize; pageIdx != pageIdxEnd; ++pageIdx)
{
m_addrRange.UnmapPage(pageIdx);
const size_t pageBitmapElemBitSize = (sizeof(uint32) * 8);
size_t pageSegment = pageIdx / pageBitmapElemBitSize;
uint32 pageMask = ~(1U << static_cast<uint32>(pageIdx % pageBitmapElemBitSize));
m_pageBitmap[pageSegment] &= pageMask;
}
}
void CPageMappingHeap::Init()
{
UINT_PTR start = (UINT_PTR)m_addrRange.GetBaseAddress();
UINT_PTR end = start + m_addrRange.GetPageCount() * m_addrRange.GetPageSize();
size_t addressSpace = end - start;
size_t pageSize = m_addrRange.GetPageSize();
size_t numPages = (addressSpace + pageSize - 1) / pageSize;
m_pageBitmap.resize((numPages + 31) / 32);
size_t pageCapacity = m_pageBitmap.size() * 32;
size_t numUnavailablePages = pageCapacity - numPages;
if (numUnavailablePages > 0)
{
m_pageBitmap.back() = ~((1 << (32 - numUnavailablePages)) - 1);
}
}