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/StreamEngine/StreamAsyncFileRequest.cpp

1028 lines
27 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 "StreamAsyncFileRequest.h"
#include <zlib.h>
#include "StreamEngine.h"
#include "../System.h"
#include <CryPath.h>
#include <AzFramework/Archive/Archive.h>
extern CMTSafeHeap* g_pPakHeap;
#if defined(STREAMENGINE_ENABLE_STATS)
extern SStreamEngineStatistics* g_pStreamingStatistics;
#endif
extern SStreamEngineOpenStats* g_pStreamingOpenStatistics;
volatile int CAsyncIOFileRequest::s_nLiveRequests;
SLockFreeSingleLinkedListHeader CAsyncIOFileRequest::s_freeRequests;
#ifdef STREAMENGINE_ENABLE_LISTENER
class NotifyListenerIO
{
public:
NotifyListenerIO(IStreamEngineListener* pL, CAsyncIOFileRequest* pReq, AZ::IO::CCachedFileData* pZipEntry, uint32 readSize)
: m_pL(pL)
, m_pReq(pReq)
{
if (m_pL)
{
m_pL->OnStreamBeginIO(
m_pReq,
(pZipEntry && pZipEntry->m_pFileEntry->nMethod) ? pZipEntry->GetFileEntry()->desc.lSizeCompressed : m_pReq->m_nFileSize,
readSize,
pReq->m_eMediaType);
}
}
~NotifyListenerIO()
{
End();
}
void End()
{
if (m_pL)
{
m_pL->OnStreamEndIO(m_pReq);
m_pL = NULL;
}
}
private:
NotifyListenerIO(const NotifyListenerIO&);
NotifyListenerIO& operator = (const NotifyListenerIO&);
private:
IStreamEngineListener* m_pL;
CAsyncIOFileRequest* m_pReq;
};
#endif //STREAMENGINE_ENABLE_LISTENER
//////////////////////////////////////////////////////////////////////////
void* CAsyncIOFileRequest::operator new (size_t sz)
{
return CryModuleMemalign(sz, alignof(CAsyncIOFileRequest));
}
void CAsyncIOFileRequest::operator delete(void* p)
{
CryModuleMemalignFree(p);
}
//////////////////////////////////////////////////////////////////////////
CAsyncIOFileRequest::CAsyncIOFileRequest()
: m_nRefCount(0)
, m_pMemoryBuffer(NULL)
{
Reset();
}
//////////////////////////////////////////////////////////////////////////
CAsyncIOFileRequest::~CAsyncIOFileRequest()
{
#ifndef _RELEASE
if (m_pMemoryBuffer)
{
__debugbreak();
}
if (m_eType)
{
__debugbreak();
}
#endif
}
//////////////////////////////////////////////////////////////////////////
uint32 CAsyncIOFileRequest::ConfigureRead(AZ::IO::CCachedFileData* pFileData)
{
AZ::IO::ZipDir::FileEntry* pFileEntry = pFileData ? pFileData->m_pFileEntry : NULL;
if (!pFileData || !pFileEntry->IsCompressed())
{
m_bCompressedBuffer = false;
m_nFileSizeCompressed = m_nFileSize;
m_nSizeOnMedia = m_nRequestedSize;
m_nPageReadStart = m_nRequestedOffset;
m_nPageReadEnd = m_nRequestedOffset + m_nRequestedSize;
}
else
{
m_bCompressedBuffer = true;
m_nFileSize = pFileEntry->desc.lSizeUncompressed;
m_nFileSizeCompressed = pFileEntry->desc.lSizeCompressed;
m_nSizeOnMedia = m_nFileSizeCompressed;
m_nPageReadStart = 0;
m_nPageReadEnd = m_nFileSizeCompressed;
}
if (pFileData && !m_bWriteOnlyExternal)
{
m_crc32FromHeader = pFileEntry->desc.lCRC32;
}
m_nPageReadCurrent = 0;
m_bStreamInPlace = !m_bCompressedBuffer || ((!m_pExternalMemoryBuffer || !m_bWriteOnlyExternal) && (m_nFileSize > m_nFileSizeCompressed));
m_bReadBegun = 1;
return 0;
}
bool CAsyncIOFileRequest::CanReadInPages()
{
bool bReadInBlocks = false;
if (g_cvars.sys_streaming_in_blocks)
{
//stream is compressed and uncompressed size is greater than one page
if (!m_bCompressedBuffer || m_nFileSizeCompressed > STREAMING_BLOCK_SIZE)
{
bReadInBlocks = true;
}
}
return bReadInBlocks;
}
uint32 CAsyncIOFileRequest::AllocateOutput([[maybe_unused]] AZ::IO::CCachedFileData* pZipEntry)
{
if (!m_bOutputAllocated)
{
uint32 nAllocSize = 0;
uint32 nReadAllocSize = 0;
uint32 nZStreamOffs = 0;
uint32 nLookaheadOffs = 0;
if (m_pExternalMemoryBuffer)
{
nReadAllocSize = m_bCompressedBuffer
? (m_nRequestedSize < m_nFileSize ? m_nFileSize : 0)
: 0;
}
else
{
nReadAllocSize = m_bCompressedBuffer
? m_nFileSize
: m_nRequestedSize;
}
nAllocSize = Align(nReadAllocSize, BUFFER_ALIGNMENT);
bool bReadInBlocks = CanReadInPages();
bool bNeedsLookahead = m_bStreamInPlace;
bool bBlockDecompress = m_bCompressedBuffer && bReadInBlocks;
if (bBlockDecompress)
{
nZStreamOffs = nAllocSize;
nAllocSize += Align(sizeof(z_stream), BUFFER_ALIGNMENT);
if (bNeedsLookahead)
{
nLookaheadOffs = nAllocSize;
nAllocSize += Align(sizeof(*m_pLookahead), BUFFER_ALIGNMENT);
}
}
char* pBuffer = NULL;
if (nAllocSize)
{
const char* usageHint = "AsyncIO TempBuffer";
pBuffer = (char*)GetStreamEngine()->TempAlloc(nAllocSize, usageHint, true, IgnoreOutofTmpMem(), BUFFER_ALIGNMENT);
if (!pBuffer)
{
return ERROR_OUT_OF_MEMORY;
}
}
if (pBuffer)
{
m_pMemoryBuffer = pBuffer;
m_nMemoryBufferSize = nAllocSize;
}
if (nReadAllocSize)
{
m_pReadMemoryBuffer = pBuffer;
m_nReadMemoryBufferSize = nReadAllocSize;
}
else
{
m_pReadMemoryBuffer = m_pExternalMemoryBuffer;
m_nReadMemoryBufferSize = m_nRequestedSize;
}
m_pOutputMemoryBuffer = m_pExternalMemoryBuffer
? m_pExternalMemoryBuffer
: m_pReadMemoryBuffer;
if (bBlockDecompress)
{
m_pZlibStream = (z_stream*)&pBuffer[nZStreamOffs];
memset(m_pZlibStream, 0, sizeof(z_stream));
if (bNeedsLookahead)
{
m_pLookahead = new (&pBuffer[nLookaheadOffs]) AZ::IO::ZipDir::UncompressLookahead;
}
m_pZlibStream->zalloc = CMTSafeHeap::StaticAlloc;
m_pZlibStream->zfree = CMTSafeHeap::StaticFree;
m_pZlibStream->opaque = g_pPakHeap;
}
if (m_bCompressedBuffer)
{
m_pDecompQueue = new SStreamJobQueue;
}
// Doesn't need to be atomic, as there's no concurrency yet.
int nMemoryBufferUsers = 0;
if (nReadAllocSize > 0)
{
++nMemoryBufferUsers;
}
if (m_bCompressedBuffer)
{
++nMemoryBufferUsers;
}
m_nMemoryBufferUsers = nMemoryBufferUsers;
m_bOutputAllocated = 1;
}
return 0;
}
byte* CAsyncIOFileRequest::AllocatePage(size_t sz, bool bOnlyPakMem, SStreamPageHdr*& pHdrOut)
{
const char* usageHint = "streaming page";
size_t nSzAligned = Align(sz, BUFFER_ALIGNMENT);
size_t nToAlloc = nSzAligned + sizeof(SStreamPageHdr);
byte* pRet = (byte*)GetStreamEngine()->TempAlloc(nToAlloc, usageHint, true, !bOnlyPakMem, BUFFER_ALIGNMENT);
if (pRet)
{
pHdrOut = new (pRet + nSzAligned)SStreamPageHdr(nToAlloc);
}
return pRet;
}
//////////////////////////////////////////////////////////////////////////
void CAsyncIOFileRequest::Cancel()
{
if (!HasFailed())
{
CryOptionalAutoLock<CryCriticalSection> readLock(m_externalBufferLockRead, m_pExternalMemoryBuffer != NULL);
CryOptionalAutoLock<CryCriticalSection> decompLock(m_externalBufferLockDecompress, m_pExternalMemoryBuffer != NULL);
Failed(ERROR_USER_ABORT);
}
}
void CAsyncIOFileRequest::SyncWithDecompress()
{
m_decompJobExecutor.reset(); // destructor waits on job completion
}
//////////////////////////////////////////////////////////////////////////
bool CAsyncIOFileRequest::TryCancel()
{
if (!HasFailed())
{
bool bExt = false;
if (m_pExternalMemoryBuffer != NULL)
{
if (!m_externalBufferLockRead.TryLock())
{
return false;
}
if (!m_externalBufferLockDecompress.TryLock())
{
m_externalBufferLockRead.Unlock();
return false;
}
bExt = true;
}
Failed(ERROR_USER_ABORT);
if (bExt)
{
m_externalBufferLockDecompress.Unlock();
m_externalBufferLockRead.Unlock();
}
}
return true;
}
//////////////////////////////////////////////////////////////////////////
void CAsyncIOFileRequest::FreeBuffer()
{
if (m_pZlibStream)
{
//if the stream was cancelled in flight, inform zlib to free internal allocs
if (m_pZlibStream->state)
{
inflateEnd(m_pZlibStream);
}
m_pZlibStream = NULL;
}
m_pLookahead = NULL;
SStreamEngineTempMemStats& tms = GetStreamEngine()->GetTempMemStats();
if (m_pDecompQueue)
{
m_pDecompQueue->Flush(tms);
delete m_pDecompQueue;
m_pDecompQueue = NULL;
}
if (m_pMemoryBuffer)
{
CStreamEngine* pStreamEngine = GetStreamEngine();
pStreamEngine->TempFree(m_pMemoryBuffer, m_nMemoryBufferSize);
m_pMemoryBuffer = 0;
}
#ifdef STREAMENGINE_ENABLE_STATS
// Update Streaming statistics.
if (g_pStreamingStatistics && m_nSizeOnMedia != 0 && m_bStatsUpdated)
{
m_bStatsUpdated = false;
int nSize = (int)m_nSizeOnMedia;
SStreamEngineStatistics& stats = *g_pStreamingStatistics;
CryInterlockedAdd(&stats.nPendingReadBytes, -nSize);
CryInterlockedAdd(&stats.typeInfo[m_eType].nPendingReadBytes, -nSize);
}
#endif
}
CAsyncIOFileRequest* CAsyncIOFileRequest::Allocate(EStreamTaskType eType)
{
CAsyncIOFileRequest* pReq = static_cast<CAsyncIOFileRequest*>(CryInterlockedPopEntrySList(s_freeRequests));
IF_UNLIKELY (!pReq)
{
pReq = new CAsyncIOFileRequest;
}
pReq->Init(eType);
return pReq;
}
void CAsyncIOFileRequest::Flush()
{
for (CAsyncIOFileRequest* pReq = static_cast<CAsyncIOFileRequest*>(CryInterlockedPopEntrySList(s_freeRequests));
pReq;
pReq = static_cast<CAsyncIOFileRequest*>(CryInterlockedPopEntrySList(s_freeRequests)))
{
delete pReq;
}
}
void CAsyncIOFileRequest::Reset()
{
m_decompJobExecutor.reset(); // destructor waits on job completion
#ifndef _RELEASE
if (m_pMemoryBuffer)
{
__debugbreak();
}
#endif
m_pReadStream = NULL;
m_strFileName.resize(0);
m_pakFile.resize(0);
// Reset POD members of the structure
memset(&m_nSortKey, 0, ((char*)(this + 1) - (char*)&m_nSortKey));
}
void CAsyncIOFileRequest::Init(EStreamTaskType eType)
{
#ifndef _RELEASE
if (!eType)
{
__debugbreak();
}
#endif
m_eType = eType;
#ifdef STREAMENGINE_ENABLE_STATS
m_startTime = gEnv->pTimer->GetAsyncTime();
#endif
if (g_pStreamingOpenStatistics)
{
SStreamEngineOpenStats& stats = *g_pStreamingOpenStatistics;
CryInterlockedIncrement(&stats.nOpenRequestCount);
CryInterlockedIncrement(&stats.nOpenRequestCountByType[eType]);
}
CryInterlockedIncrement(&s_nLiveRequests);
}
void CAsyncIOFileRequest::Finalize()
{
#ifndef _RELEASE
if (!m_eType)
{
__debugbreak();
}
#endif
#ifdef STREAMENGINE_ENABLE_LISTENER
IStreamEngineListener* pListener = gEnv->pSystem->GetStreamEngine()->GetListener();
if (pListener)
{
pListener->OnStreamDone(this);
}
#endif
if (g_pStreamingOpenStatistics)
{
SStreamEngineOpenStats& stats = *g_pStreamingOpenStatistics;
CryInterlockedDecrement(&stats.nOpenRequestCount);
CryInterlockedDecrement(&stats.nOpenRequestCountByType[m_eType]);
}
CryInterlockedDecrement(&s_nLiveRequests);
FreeBuffer();
Reset();
}
uint32 CAsyncIOFileRequest::OpenFile(CCryFile& file)
{
auto* pIPak = gEnv ? gEnv->pCryPak : NULL;
PREFAST_ASSUME(pIPak);
if (m_pReadStream && m_pReadStream->GetParams().nFlags & IStreamEngine::FLAGS_FILE_ON_DISK)
{
pIPak = 0;
}
file = CCryFile(pIPak);
if (!file.Open(m_strFileName.c_str(), "rb", AZ::IO::IArchive::FOPEN_FORSTREAMING))
{
return ERROR_CANT_OPEN_FILE;
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
uint32 CAsyncIOFileRequest::ReadFile(CStreamingIOThread* pIOThread)
{
uint32 nError = m_nError;
if (nError)
{
return nError;
}
CCryFile file;
nError = OpenFile(file);
if (nError)
{
return nError;
}
m_nFileSize = file.GetLength();
if (m_nRequestedOffset >= m_nFileSize)
{
return ERROR_OFFSET_OUT_OF_RANGE;
}
if (m_nRequestedOffset + m_nRequestedSize > m_nFileSize)
{
return ERROR_SIZE_OUT_OF_RANGE;
}
if (m_nRequestedSize == 0)
{
// by default, we read the whole file
m_nRequestedSize = m_nFileSize - m_nRequestedOffset;
}
if (!m_pExternalMemoryBuffer && m_pReadStream)
{
bool bAbortOnFailToAlloc = false;
CReadStream* pReadStream = static_cast<CReadStream*>(&*m_pReadStream);
m_pExternalMemoryBuffer = pReadStream->OnNeedStorage(m_nFileSize, bAbortOnFailToAlloc);
if (!m_pExternalMemoryBuffer && bAbortOnFailToAlloc)
{
Cancel();
m_pReadStream->Abort();
return ERROR_USER_ABORT;
}
}
if (HasFailed())
{
return m_nError;
}
auto pZipEntry = ((AZ::IO::Archive*)(gEnv->pCryPak))->GetOpenedFileDataInZip(file.GetHandle());
nError = ConfigureRead(pZipEntry.get());
if (nError)
{
return nError;
}
nError = AllocateOutput(pZipEntry.get());
if (nError)
{
return nError;
}
return ReadFileInPages(pIOThread, file);
}
uint32 CAsyncIOFileRequest::ReadFileResume(CStreamingIOThread* pIOThread)
{
uint32 nError = m_nError;
if (nError)
{
return nError;
}
CCryFile file;
nError = OpenFile(file);
if (nError)
{
return nError;
}
auto pZipEntry = ((AZ::IO::Archive*)(gEnv->pCryPak))->GetOpenedFileDataInZip(file.GetHandle());
nError = AllocateOutput(pZipEntry.get());
if (nError)
{
return nError;
}
#ifdef STREAMENGINE_ENABLE_LISTENER
IStreamEngineListener* pListener = gEnv->pSystem->GetStreamEngine()->GetListener();
if (pListener)
{
pListener->OnStreamResumed(this);
}
#endif
return ReadFileInPages(pIOThread, file);
}
uint32 CAsyncIOFileRequest::ReadFileInPages(CStreamingIOThread* pIOThread, CCryFile& file)
{
auto pCryPak = static_cast<AZ::IO::Archive*>(gEnv->pCryPak);
auto pZipEntry = pCryPak->GetOpenedFileDataInZip(file.GetHandle());
AZ::IO::ZipDir::Cache* pZip = NULL;
unsigned int nZipFlags = 0;
if (pZipEntry)
{
pZip = pZipEntry->GetZip();
nZipFlags = pZipEntry->m_nArchiveFlags;
}
if (pIOThread->IsMisscheduled(EStreamSourceMediaType::eStreamSourceTypeHDD))
{
// We're on the wrong IO thread!
return ERROR_MISSCHEDULED;
}
bool bReadInPages = CanReadInPages();
uint32 nPageReadLen = (m_nPageReadEnd - m_nPageReadStart);
bool const bCompressed = m_bCompressedBuffer;
bool const bInPlace = m_bStreamInPlace;
bool const bIgnoreOutOfTmp = IgnoreOutofTmpMem();
size_t const nReadStartOffset = bCompressed
? (m_nFileSize - m_nFileSizeCompressed)
: 0;
byte* const pReadBase = (byte*)m_pReadMemoryBuffer + nReadStartOffset;
byte* const pReadEnd = (byte*)m_pReadMemoryBuffer + m_nReadMemoryBufferSize;
CStreamEngine* pStreamEngine = static_cast<CStreamEngine*>(gEnv->pSystem->GetStreamEngine());
uint32 nPageSize = bReadInPages
? min((uint32)STREAMING_PAGE_SIZE, nPageReadLen - m_nPageReadCurrent)
: nPageReadLen - m_nPageReadCurrent;
while (nPageSize > 0)
{
CryOptionalAutoLock<CryCriticalSection> readLock(m_externalBufferLockRead, m_pExternalMemoryBuffer != NULL);
uint32 nError = m_nError;
if (nError)
{
return nError;
}
//check if job needs to be pre-empted
nError = ReadFileCheckPreempt(pIOThread);
if (nError)
{
return nError;
}
byte* pReadTarget = pReadBase + m_nPageReadCurrent;
byte* pReadTargetEnd = pReadTarget + nPageSize;
bool bTemporaryReadTarget = false;
SStreamPageHdr* pTemporaryPageHdr = NULL;
if (bInPlace)
{
if (pReadTargetEnd > pReadEnd)
{
__debugbreak();
}
}
else
{
pReadTarget = AllocatePage(nPageSize, !bIgnoreOutOfTmp, pTemporaryPageHdr);
if (!pReadTarget)
{
return ERROR_OUT_OF_MEMORY;
}
bTemporaryReadTarget = true;
pTemporaryPageHdr->nRefs = 1;
}
#ifndef _RELEASE
if (m_nPageReadCurrent + nPageSize > nPageReadLen)
{
__debugbreak();
}
#endif
{
#ifdef STREAMENGINE_ENABLE_LISTENER
NotifyListenerIO IOListener(gEnv->pSystem->GetStreamEngine()->GetListener(), this, pZipEntry.get(), nPageSize);
#endif
#ifdef STREAMENGINE_ENABLE_STATS
CTimeValue t0 = gEnv->pTimer->GetAsyncTime();
#endif
//printf("[StreamRead] %p %i %p %i %i\n", this, m_bCompressedBuffer, pReadTarget, m_nPageReadStart + m_nPageReadCurrent, nPageSize);
bool bReadOk = false;
if (pZipEntry)
{
bReadOk = pZipEntry->m_pZip->ReadFile(pZipEntry->m_pFileEntry, pReadTarget, nullptr) == AZ::IO::ZipDir::ZD_ERROR_SUCCESS;
}
else
{
file.Seek(m_nPageReadStart + m_nPageReadCurrent, SEEK_SET);
bReadOk = file.ReadRaw(pReadTarget, nPageSize) == nPageSize;
}
if (bReadOk)
{
//send each block to listener
#ifdef STREAMENGINE_ENABLE_LISTENER
IOListener.End();
#endif
#ifdef STREAMENGINE_ENABLE_STATS
m_readTime += gEnv->pTimer->GetAsyncTime() - t0;
#endif
//release external mem lock, allows jobs to be cancelled mid stream
readLock.Release();
}
else
{
if (bTemporaryReadTarget)
{
GetStreamEngine()->TempFree(pReadTarget, pTemporaryPageHdr->nSize);
}
return ERROR_REFSTREAM_ERROR;
}
bool bLastBlock = (m_nPageReadCurrent + nPageSize) == nPageReadLen;
if (bCompressed)
{
PushDecompressPage(pStreamEngine->GetJobEngineState(), pReadTarget, pTemporaryPageHdr, nPageSize, bLastBlock);
}
else if (bTemporaryReadTarget)
{
__debugbreak();
}
if (pTemporaryPageHdr && CryInterlockedDecrement(&pTemporaryPageHdr->nRefs) == 0)
{
GetStreamEngine()->TempFree(pReadTarget, pTemporaryPageHdr->nSize);
}
m_nPageReadCurrent += nPageSize;
nPageSize = min((uint32)STREAMING_PAGE_SIZE, nPageReadLen - m_nPageReadCurrent);
}
}
return 0;
}
uint32 CAsyncIOFileRequest::ReadFileCheckPreempt(CStreamingIOThread* pIOThread)
{
if (m_ePriority != estpUrgent)
{
if (pIOThread->HasUrgentRequests())
{
//printf("Read Job %s pre-empted mid stream. Progress %d / %d bytes\n", m_strFileName.c_str(), m_nBytesRead, m_nFileSizeCompressed);
#ifdef STREAMENGINE_ENABLE_LISTENER
IStreamEngineListener* pListener = gEnv->pSystem->GetStreamEngine()->GetListener();
if (pListener)
{
pListener->OnStreamPreempted(this);
}
#endif
return ERROR_PREEMPTED;
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
CStreamEngine* CAsyncIOFileRequest::GetStreamEngine()
{
return (CStreamEngine*)GetISystem()->GetStreamEngine();
}
//////////////////////////////////////////////////////////////////////////
EStreamSourceMediaType CAsyncIOFileRequest::GetMediaType()
{
EStreamSourceMediaType mediaType = m_eMediaType;
if (mediaType == eStreamSourceTypeUnknown)
{
if (m_bSortKeyComputed)
{
return mediaType;
}
if (m_strFileName.empty())
{
mediaType = eStreamSourceTypeMemory;
return mediaType;
}
mediaType = gEnv->pCryPak->GetFileMediaType(m_strFileName.c_str());
}
return mediaType;
}
//////////////////////////////////////////////////////////////////////////
void CAsyncIOFileRequest::ComputeSortKey(uint64 nCurrentKeyInProgress)
{
if (m_bSortKeyComputed)
{
return;
}
m_bSortKeyComputed = true;
if (m_strFileName.empty())
{
m_eMediaType = eStreamSourceTypeMemory;
m_nSortKey = m_ePriority;
return;
}
if (HasFailed())
{
m_nSortKey = 0;
return;
}
const int MaxPath = 0x800;
char szFullPathBuf[MaxPath];
const char* szFullPath = gEnv->pCryPak->AdjustFileName(m_strFileName.c_str(), szFullPathBuf, AZ_ARRAY_SIZE(szFullPathBuf), AZ::IO::IArchive::FOPEN_HINT_QUIET);
auto pCryPak = static_cast<AZ::IO::Archive*>(gEnv->pCryPak);
AZ::IO::ZipDir::CachePtr pZip = 0;
unsigned int archFlags = 0;
AZ::IO::ZipDir::FileEntry* pFileEntry = NULL;
// tests if the given file path refers to an existing file inside registered (opened) packs
// the path must be absolute normalized lower-case with forward-slashes
AZ::IO::ArchiveLocationPriority varPakPriority = pCryPak->GetPakPriority();
bool willOpenFromPak = (varPakPriority != AZ::IO::ArchiveLocationPriority::ePakPriorityFileFirst)
|| !AZ::IO::FileIOBase::GetDirectInstance()->Exists(szFullPath);
if (willOpenFromPak)
{
pFileEntry = pCryPak->FindPakFileEntry(szFullPath, archFlags, &pZip, false);
}
EStreamSourceMediaType ssmt = pCryPak->GetFileMediaType(szFullPath);
if (pFileEntry)
{
pZip->Refresh(pFileEntry);
m_nDiskOffset = (uint64)pFileEntry->nFileDataOffset;
m_nSizeOnMedia = pFileEntry->desc.lSizeCompressed;
m_nFileSize = pFileEntry->desc.lSizeUncompressed;
m_pakFile = pZip->GetFilePath();
}
m_eMediaType = ssmt;
if (ssmt != eStreamSourceTypeMemory)
{
int32 nCurrentSweep = (nCurrentKeyInProgress >> 30) & ((1 << 10) - 1);
int32 nCurrentTG = (nCurrentKeyInProgress >> 40) & ((1 << 20) - 1);
if (pFileEntry && !pFileEntry->IsCompressed())
{
m_nDiskOffset += m_nRequestedOffset;
}
// group items by priority, then by snapped request time, then sort by disk offset
m_nTimeGroup = (uint64)(gEnv->pTimer->GetAsyncTime().GetSeconds() / max(1, g_cvars.sys_streaming_requests_grouping_time_period));
m_nSweep = (m_nTimeGroup == nCurrentTG)
? nCurrentSweep
: 0;
uint64 nPrioriry = m_ePriority;
int64 nDiskOffsetKB = m_nDiskOffset >> 10; // KB
m_nSortKey = (nDiskOffsetKB) | (((uint64)m_nTimeGroup) << 40) | ((uint64)m_nSweep << 30) | (nPrioriry << 60);
// make sure we do not break incremental head movement within time group on every new request
if (m_nSortKey <= nCurrentKeyInProgress)
{
++m_nSweep;
m_nSortKey = (nDiskOffsetKB) | (((uint64)m_nTimeGroup) << 40) | ((uint64)m_nSweep << 30) | (nPrioriry << 60);
}
}
else
{
m_nSortKey = m_ePriority;
}
#if defined(STREAMENGINE_ENABLE_STATS)
// Update Streaming statistics.
if (!m_bStatsUpdated && g_pStreamingStatistics && m_nSizeOnMedia != 0)
{
m_bStatsUpdated = true;
SStreamEngineStatistics& stats = *g_pStreamingStatistics;
// if the file is not compressed then it will only read the requested size
uint32 nReadSize = m_nSizeOnMedia;
if (m_nSizeOnMedia == m_nFileSize)
{
nReadSize = m_nRequestedSize;
}
CryInterlockedAdd(&stats.nPendingReadBytes, nReadSize);
CryInterlockedAdd(&stats.typeInfo[m_eType].nPendingReadBytes, nReadSize);
}
#endif
}
void CAsyncIOFileRequest::SetPriority(EStreamTaskPriority estp)
{
m_ePriority = estp;
if (m_eMediaType != eStreamSourceTypeMemory)
{
m_nSortKey &= ~(15ULL << 60);
m_nSortKey |= static_cast<uint64>(estp) << 60;
}
else
{
m_nSortKey = m_ePriority;
}
}
void CAsyncIOFileRequest::BumpSweep()
{
++m_nSweep;
if (m_eMediaType != eStreamSourceTypeMemory)
{
m_nSortKey += 1 << 30;
}
}
//////////////////////////////////////////////////////////////////////////
bool CAsyncIOFileRequest::IgnoreOutofTmpMem() const
{
if (m_pReadStream &&
(m_pReadStream->GetParams().ePriority == estpUrgent ||
m_pReadStream->GetParams().nFlags & IStreamEngine::FLAGS_IGNORE_TMP_OUT_OF_MEM))
{
return true;
}
return false;
}
SStreamRequestQueue::SStreamRequestQueue()
{
m_requests.reserve(4096);
}
SStreamRequestQueue::~SStreamRequestQueue()
{
Reset();
}
void SStreamRequestQueue::Reset()
{
CryAutoLock<CryCriticalSection> l(m_lock);
for (size_t i = 0, c = m_requests.size(); i != c; ++i)
{
m_requests[i]->Release();
}
m_requests.clear();
}
bool SStreamRequestQueue::IsEmpty() const
{
return m_requests.empty();
}
bool SStreamRequestQueue::TryPopRequest(CAsyncIOFileRequest_AutoPtr& pOut)
{
CryAutoLock<CryCriticalSection> l(m_lock);
if (!m_requests.empty())
{
pOut = m_requests.front();
pOut->Release();
m_requests.erase(m_requests.begin());
return true;
}
return false;
}
void* SStreamEngineTempMemStats::TempAlloc(CMTSafeHeap* pHeap, size_t nSize, const char* szDbgSource, bool bFallBackToMalloc, bool bUrgent, uint32 align)
{
// Only allow falling back to malloc if the size fits within the stream budget, the request is urgent, or the temp memory is 0 - for those files that are over budget on their own.
long nInUse = m_nTempAllocatedMemory;
bFallBackToMalloc =
bUrgent
|| (bFallBackToMalloc && ((nInUse == 0) || (nInUse + static_cast<long>(nSize) <= m_nTempMemoryBudget)))
;
void* p = pHeap->TempAlloc(nSize, szDbgSource, bFallBackToMalloc, align);
#if MTSAFE_USE_GENERAL_HEAP
bool bInGenHeap = pHeap->IsInGeneralHeap(p);
#else
bool bInGenHeap = false;
#endif
if (p && !bInGenHeap)
{
ReportTempMemAlloc(nSize, 0, false);
}
return p;
}