blob: 2306aee892c81e656143e3f31bb71a78c84773ea [file] [log] [blame]
/*
* Copyright (c) 2015-2017, Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
//!
//! \file mhw_block_manager.c
//! \brief This modules implements memory block management functions as part of MHW dynamic state heap implementation
//!
#include "mhw_block_manager.h"
#include "mhw_utilities.h"
#include "mos_os_specific.h"
MHW_BLOCK_MANAGER_PARAMS MhwBlockManagerParams_default =
{
64, // Initial number of block objects in pool
1024, // Maximum number of block objects in pool
64, // Block pool increment size
0x00080000, // Initial heap size (512k)
0x00080000, // Heap size increment (512k)
0x01000000, // Maximum overall heap size (16M)
32, // Maximum number of heaps (32) (512k x 32 = 16M)
0x0800, // Block granularity = 2k (also represents min block alignment)
0x0800 // Min free block size = 2k (to reduce fragmentation)
};
const char *szListName[MHW_BLOCK_STATE_COUNT] = {
"POOL",
"FREE",
"ALLOCATED",
"SUBMITTED",
"DELETED"
};
void Mhw_BlockManager_ReverseMergeSort_With_Index(const uint32_t *pdwSizes, int32_t iCount, uint8_t *pSortedIndex)
{
uint8_t i, n;
uint8_t *pSrc, *pDst; // Source and Destination groups (alternate)
uint8_t Index1[MHW_BLOCK_MANAGER_MAX_BLOCK_ARRAY]; // Temporary sorted indexes 1
uint8_t Index2[MHW_BLOCK_MANAGER_MAX_BLOCK_ARRAY]; // Temporary sorted indexes 2 (alternating with Index1)
uint8_t *s1, *s2; // Merge source groups 1 and 2
uint8_t n1, n2; // Merge sizes groups 1 and 2
uint8_t *d; // Merge destination
// Very simple cases
if (iCount <= 1)
{
pSortedIndex[0] = 0;
return;
}
else if (iCount == 2)
{
pSortedIndex[0] = (pdwSizes[0] < pdwSizes[1]) ? 1 : 0;
pSortedIndex[1] = 1 - pSortedIndex[0];
return;
}
// Initialize sorted index table
for (i = 0; i < iCount; i++)
{
Index1[i] = i;
}
// Start alternating buffers (last will be the actual output)
pSrc = Index1;
pDst = Index2;
// Merge sort algorithm:
// Sort will perform O(log n) passes; first pass sorts (iCount/2) groups of 2, then (iCount/4) groups of 4, and so on.
// Each pass requires traversal of the entire list - O(n)
// Algorithm is expected to be O(n * log n)
for (n = 1; n < iCount; n *= 2)
{
// Setup sorted target output
if (n*2 < iCount)
{
d = pDst;
}
else
{ // Last pass, output goes to caller
d = pSortedIndex;
}
// Group selection and merge - O(n)
s1 = pSrc - n; // First group
s2 = pSrc; // Second group
for (i = n; i < iCount; i += 2*n) // i is the offset of the 2nd group
{
s1 += n;
n1 = n;
s2 += n;
n2 = n;
// Limit size of last group
if (i + n > iCount)
{
n2 = iCount - i;
}
// Merge groups
while (n1 > 0 && n2 > 0)
{
if (pdwSizes[*s1] >= pdwSizes[*s2])
{
*(d++) = *(s1++);
n1--;
}
else
{
*(d++) = *(s2++);
n2--;
}
}
// Merge remaining items
while (n1 > 0)
{
*(d++) = *s1++;
n1--;
}
while (n2 > 0)
{
*(d++) = *s2++;
n2--;
}
}
// Copy the last group (not merge-sorted)
for (i = i - n; i < iCount; i++)
{
*(d++) = *(s2++);
}
// New pass, switch Src/Dst sorted index buffers
d = pDst;
pDst = pSrc;
pSrc = d;
}
}
void Mhw_BlockManager_ReverseMergeSort(uint32_t *pdwSizes, int32_t iCount)
{
uint8_t i, n;
uint32_t *pSrc, *pDst; // Source and Destination groups (alternate)
uint32_t Buffer1[2 * MHW_BLOCK_MANAGER_MAX_BLOCK_ARRAY]; // Temporary sorted buffer 1
uint32_t Buffer2[2 * MHW_BLOCK_MANAGER_MAX_BLOCK_ARRAY]; // Temporary sorted buffer 1
uint32_t *s1, *s2; // Merge source groups 1 and 2
uint8_t n1, n2; // Merge sizes groups 1 and 2
uint32_t *d; // Merge destination
// Very simple cases
if (iCount <= 1)
{
return;
}
else if (iCount == 2)
{
if (pdwSizes[0] < pdwSizes[1])
{
uint32_t tmp = pdwSizes[1];
pdwSizes[1] = pdwSizes[0];
pdwSizes[0] = tmp;
}
return;
}
else if (iCount > 2 * MHW_BLOCK_MANAGER_MAX_BLOCK_ARRAY)
{
return;
}
// Merge sort algorithm:
// Sort will perform O(log n) passes; first pass sorts (iCount/2) groups of 2, then (iCount/4) groups of 4, and so on.
// Each pass requires traversal of the entire list - O(n)
// Algorithm is expected to be O(n * log n)
pSrc = Buffer1;
pDst = Buffer2;
for (n = 1; n < iCount; n *= 2)
{
if (n == 1)
{
s1 = pdwSizes - 1;
s2 = pdwSizes;
}
else
{
s1 = pSrc - n;
s2 = pSrc;
}
// Setup sorted target output
if (n*2 < iCount)
{
d = pDst;
}
else
{ // Last pass, output goes to caller
d = pdwSizes;
}
// Group selection and merge - O(n)
for (i = n; i < iCount; i += 2*n) // i is the offset of the 2nd group
{
s1 += n;
n1 = n;
s2 += n;
n2 = n;
// Limit size of last group
if (i + n > iCount)
{
n2 = iCount - i;
}
// Merge groups
while (n1 > 0 && n2 > 0)
{
if (*s1 >= *s2)
{
*(d++) = *(s1++);
n1--;
}
else
{
*(d++) = *(s2++);
n2--;
}
}
// Merge remaining items
while (n1 > 0)
{
*(d++) = *s1++;
n1--;
}
while (n2 > 0)
{
*(d++) = *s2++;
n2--;
}
}
// Copy the last group (not merge-sorted)
for (i = i - n; i < iCount; i++)
{
*(d++) = *(s2++);
}
// New pass, switch Src/Dst
d = pDst;
pDst = pSrc;
pSrc = d;
}
}
MHW_BLOCK_MANAGER::MHW_BLOCK_MANAGER(PMHW_BLOCK_MANAGER_PARAMS pParams):
m_MemoryPool(sizeof(MHW_STATE_HEAP_MEMORY_BLOCK), sizeof(void *)),
m_pStateHeap(nullptr)
{
//Init Parameters
if(pParams != nullptr)
{
m_Params = *pParams;
}
else
{
m_Params = MhwBlockManagerParams_default;
}
//Init Memory block list
for (int32_t i = (int32_t)MHW_BLOCK_STATE_POOL; i < MHW_BLOCK_STATE_COUNT; i++)
{
MOS_ZeroMemory(&m_BlockList[i], sizeof(MHW_BLOCK_LIST));
m_BlockList[i].BlockState = (MHW_BLOCK_STATE) i;
m_BlockList[i].pBlockManager = this;
MOS_SecureStrcpy(m_BlockList[i].szListName, 16, szListName[i]);
}
//Extend Pool
ExtendPool(m_Params.dwPoolInitialCount);
}
MHW_BLOCK_MANAGER::~MHW_BLOCK_MANAGER()
{
}
void MHW_BLOCK_MANAGER::SetStateHeap(PMHW_STATE_HEAP pStateHeap)
{
if (pStateHeap)
{
m_pStateHeap = pStateHeap;
}
return;
}
MOS_STATUS MHW_BLOCK_MANAGER::RegisterStateHeap(
PMHW_STATE_HEAP pStateHeap)
{
MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
// Setup state heap associated with the memory block manager
if (!m_pStateHeap)
{
m_pStateHeap = pStateHeap;
}
// Indicates that state heap is now being managed by this block manager object
pStateHeap->pBlockManager = this;
// Get memory block object to represent the state heap memory (free)
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock = GetBlockFromPool();
if (!pBlock)
{
return MOS_STATUS_NO_SPACE;
}
// Setup block parameters
pBlock->pStateHeap = pStateHeap; // Points to state heap
pBlock->pHeapPrev = nullptr; // First Block in current heap
pBlock->pHeapNext = nullptr; // Last Block in current heap
pBlock->dwOffsetInStateHeap = 0; // Offset is 0 = base of state heap
pBlock->dwBlockSize = pStateHeap->dwSize; // Size of the entire state heap
FrameTrackerTokenFlat_Validate(&pBlock->trackerToken);
pBlock->bStatic = 0;
// Set first/last memory block in state heap
pStateHeap->pMemoryHead = pBlock;
pStateHeap->pMemoryTail = pBlock;
// Reset special state heap controls
pStateHeap->pDebugKernel = nullptr; // Debug kernel loaded in this heap (if ISH)
pStateHeap->pScratchSpace = nullptr; // Active scratch space in this heap (if GSH)
pStateHeap->dwScratchSpace = 0; // Current scratch space size in this heap
// Insert block at the end of the free memory pool
AttachBlock(MHW_BLOCK_STATE_FREE, pBlock, MHW_BLOCK_POSITION_TAIL);
return eStatus;
}
MOS_STATUS MHW_BLOCK_MANAGER::UnregisterStateHeap(
PMHW_STATE_HEAP pStateHeap)
{
MHW_STATE_HEAP_MEMORY_BLOCK *pBlock;
bool bReleaseHeap = true;
// Verification loop - check if heap can be freed
for (pBlock = pStateHeap->pMemoryHead; pBlock != nullptr; pBlock = pBlock->pHeapNext)
{
// Move blocks not in use to deleted queue - so they cannot be reused
// NOTE: Blocks in SUBMITTED state need to wait for completion before releasing
if (pBlock->BlockState == MHW_BLOCK_STATE_FREE ||
pBlock->BlockState == MHW_BLOCK_STATE_ALLOCATED)
{
// Update heap usage
if (pBlock->BlockState == MHW_BLOCK_STATE_FREE)
{
pStateHeap->dwFree -= pBlock->dwBlockSize;
}
else
{
pStateHeap->dwUsed -= pBlock->dwBlockSize;
}
DetachBlock(pBlock->BlockState, pBlock);
AttachBlock(MHW_BLOCK_STATE_DELETED, pBlock, MHW_BLOCK_POSITION_TAIL);
}
// Block is allocated or submitted - mark block for deletion when released
else if (pBlock->BlockState != MHW_BLOCK_STATE_DELETED)
{
pBlock->bStatic = false; // Unlock block
pBlock->bDelete = true; // Mark block for deletion when done
bReleaseHeap = false; // Heap cannot be released at this moment
}
}
// All blocks have been sucessfully deleted -> move all blocks to pool, unregister state heap
if (bReleaseHeap)
{
// Return deleted blocks back to pool
for (pBlock = pStateHeap->pMemoryHead; pBlock != nullptr; pBlock = pBlock->pHeapNext)
{
// Sanity check - all blocks must be in this state!
if (pBlock->BlockState == MHW_BLOCK_STATE_DELETED)
{
DetachBlock(MHW_BLOCK_STATE_DELETED, pBlock);
ReturnBlockToPool(pBlock);
}
else
{
// NEVER SUPPOSED TO HAPPEN DUE TO PREVIOUS LOOP
MHW_ASSERTMESSAGE("ERROR: Mhw_BlockManager_UnregisterStateHeap: Invalid state, heap blocks are supposed to be all deleted by now");
}
}
return MOS_STATUS_SUCCESS;
}
else
{ // State heap cannot be unregistered because some blocks are still in use
return MOS_STATUS_UNKNOWN;
}
}
PMHW_STATE_HEAP_MEMORY_BLOCK MHW_BLOCK_MANAGER::GetBlockFromPool()
{
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock = nullptr;
// Ran out of memory blocks... extend pool of memory block objects
if (m_BlockList[MHW_BLOCK_STATE_POOL].iCount == 0)
{
ExtendPool(m_Params.dwPoolIncrement);
}
// Retrieve block object from head of the pool
pBlock = DetachBlock(MHW_BLOCK_STATE_POOL, MHW_BLOCK_POSITION_HEAD);
return pBlock;
}
void MHW_BLOCK_MANAGER::ExtendPool(uint32_t dwCount)
{
uint32_t dwBlockID = m_MemoryPool.m_dwObjCount; // Block ID starts from the current block count
// Limits the number of memory blocks
if (m_MemoryPool.m_dwCount + dwCount > m_Params.dwPoolMaxCount)
{
dwCount = m_Params.dwPoolMaxCount - m_MemoryPool.m_dwCount;
}
// Extend pool of block objects
if (dwCount > 0)
{
// Allocate array of block objects into the pool
MHW_STATE_HEAP_MEMORY_BLOCK *pBlockArray = (MHW_STATE_HEAP_MEMORY_BLOCK *) (m_MemoryPool.Allocate(dwCount));
if (pBlockArray)
{
// Insert newly created block objects into the linked list of pool objects available to the memory block manager
for (; dwCount > 0; dwCount--, pBlockArray++)
{
pBlockArray->dwBlockSize = 0;
pBlockArray->pPrev = pBlockArray->pNext = nullptr;
pBlockArray->Reserved = dwBlockID++;
AttachBlock(MHW_BLOCK_STATE_POOL, pBlockArray, MHW_BLOCK_POSITION_TAIL);
}
}
}
}
MOS_STATUS MHW_BLOCK_MANAGER::AttachBlock(
MHW_BLOCK_STATE BlockState,
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock,
PMHW_STATE_HEAP_MEMORY_BLOCK pBlockPos)
{
PMHW_BLOCK_LIST pList;
MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
// Verify parameters - objects cannot be null, block state must be valid
if (pBlock == nullptr ||
BlockState < MHW_BLOCK_STATE_POOL ||
BlockState >= MHW_BLOCK_STATE_COUNT)
{
return MOS_STATUS_INVALID_PARAMETER;
}
// Fails if block is still attached to a list
if (pBlock->pPrev || pBlock->pNext)
{
return MOS_STATUS_INVALID_PARAMETER;
}
// Get list associated with block state; move block to the list
pList = &m_BlockList[BlockState];
BLOCK_MANAGER_CHK_STATUS(AttachBlockInternal(pList, BlockState, pBlock, pBlockPos));
return MOS_STATUS_SUCCESS;
}
MOS_STATUS MHW_BLOCK_MANAGER::AttachBlockInternal(
PMHW_BLOCK_LIST pList,
MHW_BLOCK_STATE BlockState,
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock,
PMHW_STATE_HEAP_MEMORY_BLOCK pBlockPos)
{
// Check if this is the correct list!
if (pList->BlockState != BlockState)
{
MHW_ASSERTMESSAGE("ERROR: Mhw_BlockManager_AttachBlock_Internal: Block state doesn't match the list provided");
return MOS_STATUS_INVALID_PARAMETER;
}
// Setup the block state
pBlock->BlockState = BlockState;
// Attaches to the head of the list
if (pBlockPos == MHW_BLOCK_POSITION_TAIL)
{
pBlock->pPrev = pList->pTail;
pBlock->pNext = nullptr;
}
// Attaches to the tail of the list
else if (pBlockPos == MHW_BLOCK_POSITION_HEAD)
{
pBlock->pPrev = nullptr;
pBlock->pNext = pList->pHead;
}
// Insert after block provided - ensures that it belongs to the same list
else if (pBlockPos->BlockState == BlockState)
{
pBlock->pPrev = pBlockPos;
pBlock->pNext = pBlockPos->pNext;
}
// Insertion point does not belong to the same list
else
{
MHW_ASSERTMESSAGE("ERROR: Mhw_BlockManager_AttachBlock_Internal: Reference block does not belong to the list provided");
return MOS_STATUS_INVALID_PARAMETER;
}
// Modify previous block or head of the list
if (pBlock->pPrev)
{
pBlock->pPrev->pNext = pBlock;
}
else
{
pList->pHead = pBlock;
}
// Modify next block or tail of the list
if (pBlock->pNext)
{
pBlock->pNext->pPrev = pBlock;
}
else
{
pList->pTail = pBlock;
}
// Track size and number of blocks in the list
pList->dwSize += pBlock->dwBlockSize;
pList->iCount++;
return MOS_STATUS_SUCCESS;
}
PMHW_STATE_HEAP_MEMORY_BLOCK MHW_BLOCK_MANAGER::DetachBlock(
MHW_BLOCK_STATE BlockState,
PMHW_STATE_HEAP_MEMORY_BLOCK pBlockPos)
{
PMHW_BLOCK_LIST pList;
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock = nullptr;
// Verify parameters - object cannot be null, block state must be valid
if (BlockState < MHW_BLOCK_STATE_POOL ||
BlockState >= MHW_BLOCK_STATE_COUNT)
{
return nullptr;
}
// Get list associated with block state
pList = &m_BlockList[BlockState];
// Remove block from list, performing sanity check (check if block is in correct list)
pBlock = DetachBlockInternal(pList, pBlockPos);
return pBlock;
}
PMHW_STATE_HEAP_MEMORY_BLOCK MHW_BLOCK_MANAGER::DetachBlockInternal(
PMHW_BLOCK_LIST pList,
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock)
{
if(pList == nullptr)
{
return nullptr;
}
#ifdef MHW_DYNAMIC_STATE_HEAP_LOGGING
const char *szPos = "REF ";
if (pBlock == MHW_BLOCK_POSITION_HEAD)
szPos = "HEAD";
else if (pBlock == MHW_BLOCK_POSITION_TAIL)
szPos = "TAIL";
#endif
// Get block from the head of the list
if (pBlock == MHW_BLOCK_POSITION_HEAD)
{
pBlock = pList->pHead;
}
// Get block from the tail of the list
else if (pBlock == MHW_BLOCK_POSITION_TAIL)
{
pBlock = pList->pTail;
}
// Block does not belong to correct list - ASSERT and get block from head of the list
else if (pBlock->BlockState != pList->BlockState)
{
MHW_ASSERTMESSAGE("ERROR: Mhw_BlockManager_DetachBlock_Internal: Block provided does not belong to the list provided");
pBlock = nullptr;
}
if (!pBlock)
{
return nullptr;
}
// Detach block from previous; if first, update head of the list
if (pBlock->pPrev)
{
pBlock->pPrev->pNext = pBlock->pNext;
}
else
{
pList->pHead = pBlock->pNext;
}
// Detach block from next; if last, update tail of the list
if (pBlock->pNext)
{
pBlock->pNext->pPrev = pBlock->pPrev;
}
else
{
pList->pTail = pBlock->pPrev;
}
// reset pointers - block is detached
pBlock->pNext = pBlock->pPrev = nullptr;
// track size and number of block in the list
pList->dwSize -= pBlock->dwBlockSize;
pList->iCount--;
return pBlock;
}
MOS_STATUS MHW_BLOCK_MANAGER::MoveBlock(
PMHW_BLOCK_LIST pSrcList, // Source list
PMHW_BLOCK_LIST pDstList, // Destination list
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock, // Block to be moved (or HEAD/TAIL of source list)
PMHW_STATE_HEAP_MEMORY_BLOCK pBlockPos) // Position to insert (or HEAD/TAIL of target list)
{
MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
BLOCK_MANAGER_CHK_NULL(pSrcList);
BLOCK_MANAGER_CHK_NULL(pDstList);
pBlock = DetachBlockInternal(pSrcList, pBlock);
BLOCK_MANAGER_CHK_NULL(pBlock);
eStatus = AttachBlockInternal(pDstList, pDstList->BlockState, pBlock, pBlockPos);
if (eStatus != MOS_STATUS_SUCCESS)
{
MHW_ASSERTMESSAGE("ERROR: Mhw_BlockManager_MoveBlock_Internal: Failed to move block");
AttachBlockInternal(pDstList, pDstList->BlockState, pBlock, MHW_BLOCK_POSITION_TAIL);
}
return eStatus;
}
void MHW_BLOCK_MANAGER::ReturnBlockToPool(PMHW_STATE_HEAP_MEMORY_BLOCK pBlock)
{
pBlock->dwBlockSize = 0;
pBlock->pPrev = pBlock->pNext = nullptr;
AttachBlock(MHW_BLOCK_STATE_POOL, pBlock, MHW_BLOCK_POSITION_TAIL);
return;
}
MOS_STATUS MHW_BLOCK_MANAGER::Refresh()
{
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock, pNext;
PMHW_BLOCK_LIST pList;
MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
// Refresh status of SUBMITTED blocks
pList = &m_BlockList[MHW_BLOCK_STATE_SUBMITTED];
pNext = nullptr;
for (pBlock = pList->pHead; pBlock != nullptr; pBlock = pNext)
{
// check of block may be released - if not, continue loop
// NOTE - blocks are to be inserted in the sequence of execution,
// so we may finish the search as soon as we find the first
// block still "in use" (block tag > current sync tag)
// For now we're doing an exhaustive search, but it may not be needed - once
// we find the first block still in execution, we can probably stop the search
// Save next pointer before moving the block to another queue
pNext = pBlock->pNext;
// Check if block is still in use, if so, continue search
// NOTE - the following expression avoids sync tag wrapping around MAX_INT -> 0
if (!FrameTrackerTokenFlat_IsExpired(&pBlock->trackerToken))
{
continue;
}
// Block is flagged for deletion
if (pBlock->bDelete)
{
BLOCK_MANAGER_CHK_STATUS(FreeBlock(pBlock));
}
// Block is static -> move back to allocated list
else if (pBlock->bStatic)
{
BLOCK_MANAGER_CHK_STATUS(MoveBlock(pList, &m_BlockList[MHW_BLOCK_STATE_ALLOCATED],
pBlock, MHW_BLOCK_POSITION_TAIL));
}
else
// Block is non-static -> free block
{
FreeBlock(pBlock);
}
}
return eStatus;
}
void MHW_BLOCK_MANAGER::ConsolidateBlock(
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock)
{
PMHW_STATE_HEAP_MEMORY_BLOCK pAux;
// Check input parameters
if (!pBlock || pBlock->BlockState != MHW_BLOCK_STATE_FREE)
{
return;
}
// Consolidate pBlock with previous blocks
PMHW_BLOCK_LIST pFree = &m_BlockList[MHW_BLOCK_STATE_FREE];
for (pAux = pBlock->pHeapPrev; (pAux != nullptr) && (pAux->BlockState == MHW_BLOCK_STATE_FREE); pAux = pBlock->pHeapPrev)
{
// Remove block from free block list
DetachBlock(MHW_BLOCK_STATE_FREE, pAux);
// Absorb free space into original block by adjusting offset/size
pBlock->dwOffsetInStateHeap -= pAux->dwBlockSize;
pBlock->dwBlockSize += pAux->dwBlockSize;
pFree->dwSize += pAux->dwBlockSize;
// Detach memory block from sequential memory list
pBlock->pHeapPrev = pAux->pHeapPrev;
if (pBlock->pHeapPrev)
{
pBlock->pHeapPrev->pHeapNext = pBlock;
}
else
{
pBlock->pStateHeap->pMemoryHead = pBlock;
}
// Memory block object no longer needed - return to pool after consolidation
ReturnBlockToPool(pAux);
}
// Consolidate pBlock with following blocks
for (pAux = pBlock->pHeapNext; (pAux != nullptr) && (pAux->BlockState == MHW_BLOCK_STATE_FREE); pAux = pBlock->pHeapNext)
{
// Remove block from free block list
DetachBlock(MHW_BLOCK_STATE_FREE, pAux);
// Absorb free space into original block by adjusting block size (offset remains the same)
pBlock->dwBlockSize += pAux->dwBlockSize;
pFree->dwSize += pAux->dwBlockSize;
// Detach memory block from sequential memory list
pBlock->pHeapNext = pAux->pHeapNext;
if (pBlock->pHeapNext)
{
pBlock->pHeapNext->pHeapPrev = pBlock;
}
else
{
pBlock->pStateHeap->pMemoryTail = pBlock;
}
// Memory block object no longer needed - return to pool after consolidation
ReturnBlockToPool(pAux);
}
}
MOS_STATUS MHW_BLOCK_MANAGER::AllocateBlockInternal(
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock,
uint32_t dwAlignment)
{
PMHW_STATE_HEAP pHeap;
MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
BLOCK_MANAGER_CHK_NULL(pBlock);
// Remove block from free list - if block is not in free list, this operation will fail and return nullptr
pBlock = DetachBlock( MHW_BLOCK_STATE_FREE, pBlock);
BLOCK_MANAGER_CHK_NULL(pBlock);
// Initialize block data structures
pBlock->bDelete = false;
pBlock->dwDataOffset = MOS_ALIGN_CEIL(pBlock->dwOffsetInStateHeap, dwAlignment);
pBlock->dwAlignment = pBlock->dwDataOffset - pBlock->dwOffsetInStateHeap;
pBlock->dwDataSize = pBlock->dwBlockSize - pBlock->dwAlignment;
pBlock->pDataPtr = (uint8_t*)pBlock->pStateHeap->pvLockedHeap + pBlock->dwDataOffset;
// Move block to allocated list
AttachBlock(MHW_BLOCK_STATE_ALLOCATED, pBlock, MHW_BLOCK_POSITION_TAIL);
// Update available space in heap
pHeap = pBlock->pStateHeap;
pHeap->dwFree -= pBlock->dwBlockSize;
pHeap->dwUsed += pBlock->dwBlockSize;
return MOS_STATUS_SUCCESS;
}
MOS_STATUS MHW_BLOCK_MANAGER::SplitBlockInternal(
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock,
uint32_t dwSplitSize,
uint32_t dwAlignment,
bool bBackward)
{
uint32_t dwSplitOffset = 0;
MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
PMHW_STATE_HEAP_MEMORY_BLOCK pBlockL, pBlockH;
BLOCK_MANAGER_CHK_NULL(pBlock);
// Split cannot be less than min block size allowed
dwSplitSize = MOS_MAX(dwSplitSize, m_Params.dwHeapBlockMinSize);
if (pBlock->dwBlockSize < dwSplitSize)
{
return MOS_STATUS_UNKNOWN;
}
// Verify block state
if (pBlock->BlockState <= MHW_BLOCK_STATE_POOL || // Cannot split a block object from pool (contains invalid data)
pBlock->BlockState >= MHW_BLOCK_STATE_DELETED) // Cannot split a block being deleted
{
return MOS_STATUS_INVALID_PARAMETER;
}
// Select list
PMHW_BLOCK_LIST pList = &(m_BlockList[pBlock->BlockState]);
if (bBackward)
{
// Split FROM THE END of the block (higher offset)
dwSplitOffset = MOS_ALIGN_FLOOR(pBlock->dwOffsetInStateHeap + pBlock->dwBlockSize - dwSplitSize, dwAlignment);
dwSplitOffset = MOS_ALIGN_FLOOR(dwSplitOffset, m_Params.dwHeapGranularity);
}
else
{
// Split FROM THE BEGINNING of the block (lower offset)
dwSplitOffset = MOS_ALIGN_CEIL(pBlock->dwOffsetInStateHeap, dwAlignment);
dwSplitOffset = MOS_ALIGN_CEIL(dwSplitOffset + dwSplitSize, m_Params.dwHeapGranularity);
}
// Fail if block cannot be split
if (dwSplitOffset < pBlock->dwOffsetInStateHeap + m_Params.dwHeapBlockMinSize || // First fragment is too small
pBlock->dwOffsetInStateHeap + pBlock->dwBlockSize < dwSplitOffset + m_Params.dwHeapBlockMinSize) // Second fragment is too small
{
return MOS_STATUS_UNKNOWN;
}
if (bBackward)
{
pBlockH = pBlock; // We'll keep the high end of the block
pBlockL = GetBlockFromPool();
BLOCK_MANAGER_CHK_NULL(pBlockL);
uint32_t reserved = pBlockL->Reserved;
*pBlockL = *pBlock;
pBlockL->Reserved = reserved;
if (pBlock->pPrev)
{
pBlock->pPrev->pNext = pBlockL;
}
else
{
pList->pHead = pBlockL;
}
if (pBlock->pHeapPrev)
{
pBlock->pHeapPrev->pHeapNext = pBlockL;
}
else
{
pBlock->pStateHeap->pMemoryHead = pBlockL;
}
}
else
{
pBlockL = pBlock; // We'll keep the low end of the block
pBlockH = GetBlockFromPool();
BLOCK_MANAGER_CHK_NULL(pBlockH);
uint32_t reserved = pBlockH->Reserved;
*pBlockH = *pBlock;
pBlockH->Reserved = reserved;
if (pBlock->pNext)
{
pBlock->pNext->pPrev = pBlockH;
}
else
{
pList->pTail = pBlockH;
}
if (pBlock->pHeapNext)
{
pBlock->pHeapNext->pHeapPrev = pBlockH;
}
else
{
pBlock->pStateHeap->pMemoryTail = pBlockH;
}
}
// Update block adjacency list
pBlockL->pHeapNext = pBlockH;
pBlockH->pHeapPrev = pBlockL;
// Ensures that the new block is tracked in the same list as the parent block, update block count
pList->iCount++;
pBlockL->pNext = pBlockH;
pBlockH->pPrev = pBlockL;
// Adjust Block sizes
pBlockL->dwBlockSize = dwSplitOffset - pBlockL->dwOffsetInStateHeap; // Updates L block size based on split offset
pBlockH->dwOffsetInStateHeap = dwSplitOffset; // Sets 2nd block offset
pBlockH->dwBlockSize -= pBlockL->dwBlockSize; // Updates H block size by subtracting L block size
// Adjust Block data related pointers/sizes only if block is not free
if (pBlockL->BlockState != MHW_BLOCK_STATE_FREE)
{
pBlockL->dwDataSize -= pBlockH->dwBlockSize; // Removes size of new block from amount of data available
pBlockH->dwDataOffset = MOS_ALIGN_CEIL(dwSplitOffset, dwAlignment); // Adjust offset to data (accounting for alignment)
pBlockH->dwAlignment = pBlockH->dwDataOffset - dwSplitOffset; // Calculate alignment shift
pBlockH->dwDataSize = pBlockH->dwBlockSize - dwAlignment; // Adjust amount of data available
pBlockH->pDataPtr = (uint8_t*)pBlockH->pStateHeap->pvLockedHeap + pBlockH->dwDataOffset; // Setup pointer to data (the heap is locked)
}
return eStatus;
}
MOS_STATUS MHW_BLOCK_MANAGER::MergeBlocksInternal(
PMHW_STATE_HEAP_MEMORY_BLOCK pBlockL, // block in lower memory
PMHW_STATE_HEAP_MEMORY_BLOCK pBlockH, // block in higher memory
uint32_t dwAlignment, // final block alignment
bool bBackward) // true if pBlockL (free) is merged into pBlockH; false if pBlockH (free) is merged into pBlockL
{
PMHW_BLOCK_LIST pList;
MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
BLOCK_MANAGER_CHK_NULL(pBlockL);
BLOCK_MANAGER_CHK_NULL(pBlockH);
// Blocks must be contiguous in memory
if (pBlockL->pHeapNext != pBlockH ||
pBlockH->pHeapPrev != pBlockL)
{
return MOS_STATUS_INVALID_PARAMETER;
}
// 1st block (L) is merged into 2nd (H)
if (bBackward)
{
if (pBlockL->BlockState != MHW_BLOCK_STATE_FREE || // 1st block must be free
pBlockH->BlockState < MHW_BLOCK_STATE_FREE || // 2nd block cannot be in pool
pBlockH->BlockState > MHW_BLOCK_STATE_SUBMITTED) // or deleted
{
return MOS_STATUS_INVALID_PARAMETER;
}
// Merge blocks
pBlockL = DetachBlock(MHW_BLOCK_STATE_FREE, pBlockL);
BLOCK_MANAGER_CHK_NULL(pBlockL);
pBlockH->dwOffsetInStateHeap = pBlockL->dwOffsetInStateHeap;
pBlockH->dwBlockSize += pBlockL->dwBlockSize;
// Add size to the target block list
pList = &(m_BlockList[pBlockH->BlockState]);
pList->dwSize += pBlockL->dwBlockSize;
// If block allocated or submitted, adjust data references (don't care if block is free)
if (pBlockH->BlockState != MHW_BLOCK_STATE_FREE)
{
pBlockH->dwDataOffset = MOS_ALIGN_CEIL(pBlockH->dwOffsetInStateHeap, dwAlignment);
pBlockH->dwAlignment = pBlockH->dwDataOffset - pBlockH->dwOffsetInStateHeap;
pBlockH->dwDataSize = pBlockH->dwBlockSize - pBlockH->dwAlignment;
pBlockH->pDataPtr = (uint8_t*)pBlockH->pStateHeap->pvLockedHeap + pBlockH->dwDataOffset;
// Free block is now in use - track heap usage
pBlockH->pStateHeap->dwFree -= pBlockL->dwBlockSize;
pBlockH->pStateHeap->dwUsed += pBlockL->dwBlockSize;
}
// Return block object to the pool
ReturnBlockToPool(pBlockL);
}
else
// 2nd block (H) is merged into 1st (L)
{
if (pBlockH->BlockState != MHW_BLOCK_STATE_FREE || // 2nd block must be free
pBlockL->BlockState < MHW_BLOCK_STATE_FREE || // 1nd block must not be in pool
pBlockL->BlockState > MHW_BLOCK_STATE_SUBMITTED) // or deleted
{
return MOS_STATUS_INVALID_PARAMETER;
}
// Merge blocks
pBlockH = DetachBlock(MHW_BLOCK_STATE_FREE, pBlockH);
BLOCK_MANAGER_CHK_NULL(pBlockH);
pBlockL->dwBlockSize += pBlockH->dwBlockSize;
if (pBlockL->BlockState != MHW_BLOCK_STATE_FREE)
{
pBlockL->dwDataSize += pBlockH->dwBlockSize;
pBlockL->pStateHeap->dwFree -= pBlockL->dwBlockSize;
pBlockL->pStateHeap->dwUsed += pBlockL->dwBlockSize;
}
// Add size to the target block list
pList = &(m_BlockList[pBlockL->BlockState]);
pList->dwSize += pBlockH->dwBlockSize;
// Return block object to the pool
ReturnBlockToPool(pBlockH);
}
return eStatus;
}
MOS_STATUS MHW_BLOCK_MANAGER::ResizeBlock(
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock,
uint32_t dwNewSize,
uint32_t dwAlignment,
bool bBackward) // false => Always grow/shrink forward; true => allow block to grow forward/backwards (moving its start offset)
{
MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
PMHW_STATE_HEAP_MEMORY_BLOCK pNewBlock;
BLOCK_MANAGER_CHK_NULL(pBlock);
MHW_ASSERT(dwNewSize > 0);
// Verify block state
if (pBlock->BlockState <= MHW_BLOCK_STATE_POOL || // Cannot touch a block object from pool - invalid data
pBlock->BlockState >= MHW_BLOCK_STATE_DELETED) // Cannot touch a block being deleted
{
return MOS_STATUS_INVALID_PARAMETER;
}
// Shrinking current block
if (dwNewSize < pBlock->dwBlockSize)
{
// Split block into 2 - (bBackwards -> shrink the block by keeping the 2nd half (anchor end of the block)
eStatus = SplitBlockInternal(pBlock, dwNewSize, dwAlignment, bBackward);
if (eStatus != MOS_STATUS_SUCCESS)
{
// This error just means that the block cannot shrink - not an actual issue here
if (eStatus == MOS_STATUS_UNKNOWN)
{
eStatus = MOS_STATUS_SUCCESS;
return eStatus;
}
}
// Select block to be freed
pBlock = (bBackward) ? pBlock->pPrev : pBlock->pNext;
BLOCK_MANAGER_CHK_NULL(pBlock);
if (pBlock->BlockState == MHW_BLOCK_STATE_SUBMITTED)
{
// mark block for release as soon it is no longer in use by GPU
pBlock->bStatic = false;
}
else
{
// Free block - block is in allocated state
FreeBlock(pBlock);
}
// Success!
return MOS_STATUS_SUCCESS;
}
// Check for contiguous available space in forward direction first
uint32_t dwAvailable = pBlock->dwDataSize;
for (pNewBlock = pBlock->pHeapNext;
(dwAvailable < dwNewSize) && (pNewBlock) && (pNewBlock->BlockState == MHW_BLOCK_STATE_FREE);
pNewBlock = pNewBlock->pHeapNext)
{
dwAvailable += pNewBlock->dwBlockSize;
}
// Check for contiguous available space in backward direction
if (bBackward)
{
// Update available space to account for block alignment
dwAvailable += pBlock->dwAlignment - dwAlignment;
for (pNewBlock = pBlock->pHeapPrev;
(dwAvailable < dwNewSize) && (pNewBlock) && (pNewBlock->BlockState == MHW_BLOCK_STATE_FREE);
pNewBlock = pNewBlock->pHeapPrev)
{
dwAvailable += pNewBlock->dwBlockSize;
}
}
// Check if block can be resized
if (dwAvailable < dwNewSize)
{
return MOS_STATUS_UNKNOWN;
}
// Start block expansion forward
for (pNewBlock = pBlock->pHeapNext;
(pBlock->dwDataSize < dwNewSize) && (pNewBlock) && (pNewBlock->BlockState == MHW_BLOCK_STATE_FREE);
pNewBlock = pBlock->pHeapNext)
{
// Next block is too large - split the block before merging
if (pBlock->dwDataSize + pNewBlock->dwBlockSize > dwNewSize)
{
SplitBlockInternal(pNewBlock, dwNewSize - pBlock->dwDataSize, dwAlignment, false);
}
// Merge block with next
MergeBlocksInternal(pBlock, pNewBlock, dwAlignment, false);
}
// Continue expanding backward
if (bBackward)
{
for (pNewBlock = pBlock->pHeapPrev;
(dwAvailable < dwNewSize) && (pNewBlock) && (pNewBlock->BlockState == MHW_BLOCK_STATE_FREE);
pNewBlock = pBlock->pHeapPrev)
{
// Prev block is too large - split the block before merging
uint32_t dwAdjust = MOS_ALIGN_CEIL(pNewBlock->dwOffsetInStateHeap, dwAlignment) - pNewBlock->dwOffsetInStateHeap;
if (pBlock->dwBlockSize + pNewBlock->dwBlockSize - dwAdjust > dwNewSize)
{
SplitBlockInternal(pNewBlock, dwNewSize - pBlock->dwBlockSize, dwAlignment, true);
}
// Merge block with previous
MergeBlocksInternal(pNewBlock, pBlock, dwAlignment, true);
}
}
return eStatus;
}
PMHW_STATE_HEAP_MEMORY_BLOCK MHW_BLOCK_MANAGER::AllocateWithScratchSpace(
uint32_t dwSize,
uint32_t dwAlignment,
uint32_t dwScratchSpace)
{
MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock = nullptr;
PMHW_STATE_HEAP_MEMORY_BLOCK pScratch = nullptr;
// Fix alignment - must be a power of 2
if (dwAlignment < m_Params.dwHeapGranularity)
{
// Blocks are already aligned
dwAlignment = 1;
}
else
{
dwAlignment--;
dwAlignment |= dwAlignment >> 1;
dwAlignment |= dwAlignment >> 2;
dwAlignment |= dwAlignment >> 4;
dwAlignment |= dwAlignment >> 8;
dwAlignment |= dwAlignment >> 16;
dwAlignment++;
}
// Try to search search state heap with large enough scratch space
// and with enough free space
PMHW_STATE_HEAP pNextStateHeap;
for (PMHW_STATE_HEAP pStateHeap = m_pStateHeap; (pStateHeap); pStateHeap = pNextStateHeap)
{
// Save next state heap
pNextStateHeap = pStateHeap->pNext;
// Space needed for block (accounting for alignment and for scratch space)
uint32_t dwBlockNeeded = MOS_ALIGN_CEIL(dwSize + dwAlignment - 1, m_Params.dwHeapGranularity);
// Scratch space needed = space requested + room for alignment - space already allocated
uint32_t dwScratchNeeded = 0;
if (dwScratchSpace > 0 && pStateHeap->dwScratchSpace < dwScratchSpace)
{
dwScratchNeeded = dwScratchSpace;
if (m_Params.dwHeapGranularity < MHW_SCRATCH_SPACE_ALIGN)
{
dwScratchNeeded += MHW_SCRATCH_SPACE_ALIGN - m_Params.dwHeapGranularity;
}
if (pStateHeap->pScratchSpace)
{
dwScratchNeeded -= pStateHeap->pScratchSpace->dwBlockSize;
}
}
if (pStateHeap->dwSize < dwScratchNeeded)
{
// Heap is too small for current scratch space size - mark for deletion
pStateHeap->pMhwStateHeapInterface->ReleaseStateHeapDyn(pStateHeap);
continue;
}
else if (pStateHeap->dwFree < (dwScratchNeeded + dwBlockNeeded))
{
// Heap can still be used, but currently full, try next heap
continue;
}
// Allocate scratch space first
if (dwScratchNeeded)
{
pScratch = nullptr;
// Already present - EXPAND SCRATCH SPACE
if (pStateHeap->pScratchSpace)
{
// Resize existing scratch space trying to use all free space towards the end of the heap and then growing towards the center.
eStatus = ResizeBlock(pStateHeap->pScratchSpace, dwScratchSpace, MHW_SCRATCH_SPACE_ALIGN, true);
if (eStatus == MOS_STATUS_SUCCESS)
{
pScratch = pStateHeap->pScratchSpace; // Indicates success
pStateHeap->dwScratchSpace = pScratch->dwDataSize; // Available scratch space size (aligned)
}
}
// Scratch space not present or failed to expand - find a new scratch space
if (!pScratch)
{
// Search for scratch space at the end of the heap towards the beginning
// This model allows for better growth without collision with media state heaps
for (pScratch = pStateHeap->pMemoryTail; pScratch != pBlock; pScratch = pScratch->pHeapPrev)
{
if (pScratch->BlockState == MHW_BLOCK_STATE_FREE &&
pScratch->dwBlockSize >= dwScratchNeeded)
{
// Found scratch space large enough
break;
}
}
// Not enough contiguous space for scratch in heap - try next heap
if (!pScratch)
{
continue;
}
// CREATE SCRATCH SPACE
// Split block to the necessary size, using the higher portion (closer to end of the heap)
// NOTE: Block select could be much larger than needed, even the size of the entire state heap - that's why it needs to be split
eStatus = SplitBlockInternal(pScratch, dwScratchNeeded, MHW_SCRATCH_SPACE_ALIGN, true);
if (eStatus == MOS_STATUS_UNKNOWN) eStatus = MOS_STATUS_SUCCESS; // Don't care if block could not be split
if (eStatus != MOS_STATUS_SUCCESS)
{
continue;
}
// Move block to allocated list, mark it as static (do not release upon completion)
AllocateBlockInternal(pScratch, MHW_SCRATCH_SPACE_ALIGN);
pScratch->bStatic = true;
// Free the old scratch space
// NOTE: scratch spaces are also tracked by Sync Tags and maintained in submitted/allocated lists,
// If in use, it just clears the bStatic flag, so it will be freed when no longer in use.
if (pStateHeap->pScratchSpace)
{
FreeBlock(pStateHeap->pScratchSpace);
}
// Setup new heap scratch space and size
pStateHeap->pScratchSpace = pScratch;
pStateHeap->dwScratchSpace = pScratch->dwDataSize;
}
}
// Try to allocate block in the same heap as the scratch space
pBlock = AllocateBlock(dwSize, dwAlignment, pStateHeap);
if (pBlock)
{
break;
}
}
return pBlock;
}
PMHW_STATE_HEAP_MEMORY_BLOCK MHW_BLOCK_MANAGER::AllocateBlock(
uint32_t dwSize,
uint32_t dwAlignment,
PMHW_STATE_HEAP pHeapAffinity)
{
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock = nullptr;
PMHW_BLOCK_LIST pFree = &m_BlockList[MHW_BLOCK_STATE_FREE];
uint32_t dwAdjust; // Offset adjustment for alignment purposes
uint32_t dwAllocSize; // Actual allocation size accounting for alignment and other restrictions
MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
// Fix alignment - must be a power of 2 (minimum 1)
if (dwAlignment) dwAlignment--;
dwAlignment |= dwAlignment >> 1;
dwAlignment |= dwAlignment >> 2;
dwAlignment |= dwAlignment >> 4;
dwAlignment |= dwAlignment >> 8;
dwAlignment |= dwAlignment >> 16;
dwAlignment++;
// Search must include space for block granularity
if (dwAlignment <= m_Params.dwHeapGranularity)
{
// Alignment should be fulfilled by the heap granularity
dwAllocSize = dwSize;
}
else
{
// Worst case scenario - original implementation was checking alignment of
// each free block, but it was overkill - so now we just consider the worst case
dwAllocSize = dwSize + dwAlignment - m_Params.dwHeapGranularity;
}
// Enforce min block size
dwAllocSize = MOS_MAX(m_Params.dwHeapBlockMinSize, dwAllocSize);
// Search list of free blocks for the first match
for (pBlock = pFree->pHead; pBlock != nullptr; pBlock = pBlock->pNext)
{
// Skip this heap if we are looking for allocation in a specific heap
if (pHeapAffinity && pBlock->pStateHeap != pHeapAffinity)
{
continue;
}
// Check if aligned block fits the request -> break with a successful block
if (pBlock->dwBlockSize >= dwAllocSize)
{
break;
}
}
// No block was found - fail search
if (!pBlock)
{
return nullptr;
}
// Block was found, adjust the allocation size to account for
// heap granularity and block alignment
dwAdjust = MOS_ALIGN_OFFSET(pBlock->dwOffsetInStateHeap, dwAlignment); // Increase in size to align data
dwAllocSize = MOS_ALIGN_CEIL(dwSize + dwAdjust, m_Params.dwHeapGranularity); // Account for heap granularity (avoid odd addresses in heap)
dwAllocSize = MOS_MAX(dwAllocSize, m_Params.dwHeapBlockMinSize);
// Just a precaution - sanity check - in case of last block in heap, and total heap size is not a multiple of granularity
if (pBlock->dwBlockSize < dwAllocSize)
{
// This should never happend because it is part of the search condition!
MHW_ASSERT(pBlock->dwBlockSize >= (dwAdjust + dwSize));
dwAllocSize = pBlock->dwBlockSize;
}
// Split block, move to allocated list
if (pBlock->dwBlockSize > dwAllocSize)
{
// Split free block in 2, keep the first part (lower offset)
eStatus = SplitBlockInternal(pBlock, dwAllocSize, dwAlignment, false);
if (eStatus != MOS_STATUS_SUCCESS &&
eStatus != MOS_STATUS_UNKNOWN)
{
MHW_ASSERTMESSAGE("ERROR: AllocateBlock: Failed to allocate block");
return nullptr;
}
}
// Move block from free to allocated queue
DetachBlock(MHW_BLOCK_STATE_FREE, pBlock);
AttachBlock(MHW_BLOCK_STATE_ALLOCATED, pBlock, MHW_BLOCK_POSITION_TAIL);
pBlock->pStateHeap->dwUsed += pBlock->dwBlockSize;
pBlock->pStateHeap->dwFree -= pBlock->dwBlockSize;
// Reset some fields
pBlock->bDelete = false;
FrameTrackerTokenFlat_Validate(&pBlock->trackerToken);
// Setup aligned offset, size and data
pBlock->dwDataOffset = MOS_ALIGN_CEIL(pBlock->dwOffsetInStateHeap, dwAlignment);
pBlock->dwAlignment = pBlock->dwDataOffset - pBlock->dwOffsetInStateHeap;
pBlock->dwDataSize = pBlock->dwBlockSize - pBlock->dwAlignment;
pBlock->pDataPtr = (uint8_t*)pBlock->pStateHeap->pvLockedHeap + pBlock->dwDataOffset;
// return block to client
return pBlock;
}
MOS_STATUS MHW_BLOCK_MANAGER::FreeBlock(
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock)
{
MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
BLOCK_MANAGER_CHK_NULL(pBlock);
// Block still in use - mark for auto-release when complete
if (pBlock->BlockState == MHW_BLOCK_STATE_SUBMITTED)
{
// sync tag not provided or block still in use - flag it for automatic release when complete
if (!FrameTrackerTokenFlat_IsExpired(&pBlock->trackerToken))
{
pBlock->bStatic = false;
return eStatus;
}
}
else if (pBlock->BlockState != MHW_BLOCK_STATE_ALLOCATED)
{
return MOS_STATUS_INVALID_PARAMETER;
}
// Remove block from its current list
DetachBlock(pBlock->BlockState, pBlock);
// If block is marked for deletion - move to deleted list so it cannot be reallocated
if (pBlock->bDelete)
{
MHW_STATE_HEAP *pStateHeap = pBlock->pStateHeap;
pStateHeap->dwUsed -= pBlock->dwBlockSize;
AttachBlock(MHW_BLOCK_STATE_DELETED, pBlock, MHW_BLOCK_POSITION_TAIL);
// Last block was removed from StateHeap -> state heap may be unregistered and deleted
if (pStateHeap->dwUsed == 0)
{
pStateHeap->pMhwStateHeapInterface->ReleaseStateHeapDyn(pStateHeap);
}
}
else
{
// Update state heap usage
pBlock->pStateHeap->dwUsed -= pBlock->dwBlockSize;
pBlock->pStateHeap->dwFree += pBlock->dwBlockSize;
// Blocks are freed and placed at the beginning of the free block list
AttachBlock(MHW_BLOCK_STATE_FREE, pBlock, MHW_BLOCK_POSITION_TAIL);
// Consolidate memory immediately after release - so free block are ALWAYS merged
ConsolidateBlock(pBlock);
}
return eStatus;
}
uint32_t MHW_BLOCK_MANAGER::CalculateSpaceNeeded(
const uint32_t *pdwSizes,
int32_t iCount,
uint32_t dwAlignment,
bool bHeapAffinity,
PMHW_STATE_HEAP pHeapAffinity)
{
uint32_t dwNeeded = 0;
uint8_t SortedIndex[MHW_BLOCK_MANAGER_MAX_BLOCK_ARRAY];
uint32_t FreeBlockSizes[MHW_BLOCK_MANAGER_MAX_BLOCK_ARRAY * 2];
uint32_t dwBlockOverhead = 0; // Block size overhead to ensure alignment
uint32_t dwAllocSize;
// Check parameters
if (iCount <= 0 || iCount > MHW_BLOCK_MANAGER_MAX_BLOCK_ARRAY)
{
return dwNeeded;
}
// This is the minimum block
uint32_t dwBlockGranularity = m_Params.dwHeapGranularity;
uint32_t dwBlockMinSize = m_Params.dwHeapBlockMinSize;
if (dwAlignment > dwBlockGranularity)
{
dwBlockOverhead = dwAlignment - dwBlockGranularity;
}
// Very simple case - single block search
if (iCount == 1)
{
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock = m_BlockList[MHW_BLOCK_STATE_FREE].pHead;
dwNeeded = pdwSizes[0];
for ( ; pBlock != nullptr && dwNeeded > 0; pBlock = pBlock->pNext)
{
if (bHeapAffinity && pHeapAffinity != pBlock->pStateHeap)
{
continue;
}
if (dwNeeded < pBlock->dwBlockSize)
{
dwNeeded = 0;
}
}
return dwNeeded;
}
// Sort input block sizes (with index, to avoid modifying the input array)
Mhw_BlockManager_ReverseMergeSort_With_Index(pdwSizes, iCount, SortedIndex);
// Read all available blocks, keep the largest blocks
int iIndex = 0;
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock = m_BlockList[MHW_BLOCK_STATE_FREE].pHead;
for ( ; pBlock != nullptr; pBlock = pBlock->pNext)
{
// Select free blocks that fit the request
if (bHeapAffinity && pHeapAffinity != pBlock->pStateHeap) continue;
FreeBlockSizes[iIndex++] = pBlock->dwBlockSize;
// If buffer is full, sort it, only take the largest blocks (overwrite remaining blocks)
if (iIndex == MHW_BLOCK_MANAGER_MAX_BLOCK_ARRAY * 2)
{
Mhw_BlockManager_ReverseMergeSort(FreeBlockSizes, iIndex);
iIndex = iCount;
}
}
// Final sort after all block are in
Mhw_BlockManager_ReverseMergeSort(FreeBlockSizes, iIndex);
FreeBlockSizes[iIndex] = 0; // Null termination simplifies the algorithm that follows
// Start process of fitting requested blocks with available blocks
uint32_t *pFreeBlock = &FreeBlockSizes[0];
for (iIndex = 0; iIndex < iCount; iIndex++)
{
dwAllocSize = MOS_ALIGN_CEIL(pdwSizes[SortedIndex[iIndex]] + dwBlockOverhead, dwBlockGranularity);
dwAllocSize = MOS_MAX(dwAllocSize, dwBlockMinSize);
// Block doesn't fit - add to space needed
if (dwAllocSize > *pFreeBlock)
{
dwNeeded += dwAllocSize;
}
else
{
// Allocate block
uint32_t dwRemaining = *pFreeBlock = *pFreeBlock - dwAllocSize;
uint32_t *pAux;
// Remaining is out of order, keep list sorted using insertion sort
if (dwRemaining < pFreeBlock[1])
{
for (pAux = pFreeBlock; dwRemaining < pAux[1]; pAux++) pAux[0] = pAux[1];
pAux[0] = dwRemaining;
}
}
}
return dwNeeded;
}
MOS_STATUS MHW_BLOCK_MANAGER::SubmitBlock(
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock,
const FrameTrackerTokenFlat *trackerToken)
{
MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
BLOCK_MANAGER_CHK_NULL(pBlock);
if (pBlock->BlockState == MHW_BLOCK_STATE_POOL || // Block cannot be submitted directly from pool
pBlock->BlockState == MHW_BLOCK_STATE_FREE || // or from free (must be allocated first)
pBlock->BlockState == MHW_BLOCK_STATE_DELETED) // or from deleted (the block is being DELETED for crying out loud!)
{
MHW_ASSERTMESSAGE("ERROR: SubmitBlock: Block in invalid state, cannot be enqueued");
return MOS_STATUS_UNKNOWN;
}
// Detach block from whatever list it's in - could be in SUBMITTED state (block is being reused - i.e., kernel or scratch space)
pBlock = DetachBlock(pBlock->BlockState, pBlock);
BLOCK_MANAGER_CHK_NULL(pBlock);
// Set block sync tag - used for refreshing the block status
FrameTrackerTokenFlat_Merge(&pBlock->trackerToken, trackerToken);
BLOCK_MANAGER_CHK_STATUS(AttachBlock(MHW_BLOCK_STATE_SUBMITTED, pBlock, MHW_BLOCK_POSITION_TAIL));
return eStatus;
}
PMHW_STATE_HEAP_MEMORY_BLOCK MHW_BLOCK_MANAGER::AllocateMultiple(
uint32_t *pdwSizes,
int32_t iCount,
uint32_t dwAlignment,
bool bHeapAffinity,
PMHW_STATE_HEAP pHeapAffinity)
{
PMHW_STATE_HEAP pStateHeap;
uint32_t dwTotalSize = 0;
uint8_t SortedIndex[MHW_BLOCK_MANAGER_MAX_BLOCK_ARRAY]; // uint8_t is only used because the array size is <256
PMHW_STATE_HEAP_MEMORY_BLOCK pBlockArray[MHW_BLOCK_MANAGER_MAX_BLOCK_ARRAY]; // This could be allocated dynamically, same for the above
PMHW_STATE_HEAP_MEMORY_BLOCK pBlock;
// Clear the result
pBlockArray[0] = nullptr;
if (iCount <= 0 || iCount > MHW_BLOCK_MANAGER_MAX_BLOCK_ARRAY)
{
return nullptr;
}
// Get total allocation size
for (int i = 0; i < iCount; i++)
{
dwTotalSize += pdwSizes[i];
}
Mhw_BlockManager_ReverseMergeSort_With_Index(pdwSizes, iCount, SortedIndex);
if (bHeapAffinity)
{
if (pHeapAffinity)
{
// Set heap affinity - all blocks will be allocated from this one particular heap
pStateHeap = pHeapAffinity;
}
else
{
// Heap affinity is not set - start from the current active heap (most recently allocated heap)
pStateHeap = m_pStateHeap;
}
}
else
{ // Don't care about heap affinity, blocks can be spread across all available state heaps
// Just calculate total space available to make sure we have it
uint32_t dwTotalHeapSpace = 0;
for (pStateHeap = m_pStateHeap; pStateHeap != nullptr; pStateHeap = pStateHeap->pNext)
{
// Calculate total free space in all available heaps
// NOTE: Heap being deleted as free space set to 0
// and all Free blocks are now Deleted (in deleted queue)
dwTotalHeapSpace += pStateHeap->dwFree;
}
// Not enough space
if (dwTotalHeapSpace < dwTotalSize)
{
return pBlockArray[0];
}
}
// Loop to try loading all kernels into the same heap
do {
if ( (!bHeapAffinity) || // no affinity set -> blocks can be spread across multiple heaps
pStateHeap->dwFree >= dwTotalSize) // this heap has enough free space
{
int32_t i, j;
// Allocate array according to size
for (i = 0, pBlock = nullptr; i < iCount; i++)
{ // NOTE - don't care about scratch space, already checked
pBlock = pBlockArray[SortedIndex[i]] = AllocateBlock(pdwSizes[SortedIndex[i]], dwAlignment, pStateHeap);
if (!pBlock)
{
break;
}
}
// Allocations all sucessfull, quit search
if (pBlock)
{
break;
}
// One of the block allocations failed - free all blocks already allocated (try another heap)
for (j = 0; j < i; j++)
{
FreeBlock(pBlockArray[SortedIndex[j]]);
}
// Clear the output
pBlockArray[0] = nullptr;
}
// Try another heap if bHeapAffinity is set
if (bHeapAffinity)
{
pStateHeap = (pHeapAffinity) ? nullptr : pStateHeap->pNext;
}
} while (pStateHeap);
// Allocation successful, reorder blocks according to original request
if (pBlockArray[0])
{
for (int32_t i = 0; i < iCount; i++)
{
pBlock = DetachBlock(MHW_BLOCK_STATE_ALLOCATED, pBlockArray[i]);
AttachBlock(MHW_BLOCK_STATE_ALLOCATED, pBlock, MHW_BLOCK_POSITION_TAIL);
}
}
return pBlockArray[0];
}