296 lines
6.2 KiB
C++
296 lines
6.2 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
// Implementation of D3D12 Disk caching functions to preserve state across runs
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Include Files
|
|
//-----------------------------------------------------------------------------
|
|
#include "WindowsD3D12DiskCache.h"
|
|
#include "D3D12RHIPrivate.h"
|
|
|
|
void FDiskCacheInterface::Init(FString &filename, bool bEnable)
|
|
{
|
|
mFileStart = nullptr;
|
|
mFile = 0;
|
|
mMemoryMap = 0;
|
|
mMapAddress = 0;
|
|
mCurrentFileMapSize = 0;
|
|
mCurrentOffset = 0;
|
|
mInErrorState = false;
|
|
mEnableDiskCache = bEnable;
|
|
|
|
mFileName = filename;
|
|
mCacheExists = true;
|
|
if (!mEnableDiskCache)
|
|
{
|
|
mInErrorState = true;
|
|
mCacheExists = false;
|
|
}
|
|
else
|
|
{
|
|
WIN32_FIND_DATA fileData;
|
|
HANDLE Handle = FindFirstFile(mFileName.GetCharArray().GetData(), &fileData);
|
|
if (Handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
if (GetLastError() == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
mCacheExists = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FindClose(Handle);
|
|
}
|
|
}
|
|
bool fileFound = mCacheExists;
|
|
GrowMapping(64 * 1024, true);
|
|
|
|
if (fileFound && mFileStart)
|
|
{
|
|
mHeader = *(FDiskCacheHeader*)mFileStart;
|
|
if (mHeader.mHeaderVersion != mCurrentHeaderVersion)
|
|
{
|
|
UE_LOG(LogD3D12RHI, Warning, TEXT("Disk cache is stale. Disk Cache version: %d App version: %d"), mHeader.mHeaderVersion, mCurrentHeaderVersion);
|
|
ClearAndReinitialize();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mHeader.mHeaderVersion = mCurrentHeaderVersion;
|
|
mHeader.mNumPsos = 0;
|
|
mHeader.mSizeInBytes = 0;
|
|
}
|
|
}
|
|
|
|
void FDiskCacheInterface::GrowMapping(SIZE_T size, bool firstrun)
|
|
{
|
|
if (IsInErrorState())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (mCurrentOffset + size > mCurrentFileMapSize)
|
|
{
|
|
mCurrentFileMapSize = Align(mCurrentOffset + size, mFileGrowSize);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (mMapAddress)
|
|
{
|
|
FlushViewOfFile(mMapAddress, mCurrentOffset);
|
|
UnmapViewOfFile(mMapAddress);
|
|
}
|
|
if (mMemoryMap)
|
|
{
|
|
CloseHandle(mMemoryMap);
|
|
}
|
|
if (mFile)
|
|
{
|
|
CloseHandle(mFile);
|
|
}
|
|
|
|
uint32 flag = (mCacheExists) ? OPEN_EXISTING : CREATE_NEW;
|
|
// open the shader cache file
|
|
mFile = CreateFile(mFileName.GetCharArray().GetData(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, flag, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (mFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
//error state!
|
|
mInErrorState = true;
|
|
return;
|
|
}
|
|
|
|
mCacheExists = true;
|
|
|
|
uint32 fileSize = GetFileSize(mFile, NULL);
|
|
|
|
if (fileSize == 0)
|
|
{
|
|
uint8 data[64];
|
|
FMemory::Memzero(data);
|
|
//It's invalide to map a zero sized file so write some junk data in that case
|
|
WriteFile(mFile, data, sizeof(data), NULL, NULL);
|
|
}
|
|
else if (firstrun)
|
|
{
|
|
mCurrentFileMapSize = fileSize;
|
|
}
|
|
|
|
mMemoryMap = CreateFileMapping(mFile, NULL, PAGE_READWRITE, (uint32)(mCurrentFileMapSize >> 32), (uint32)(mCurrentFileMapSize & 0xFFFFFFFF), NULL);
|
|
if (mMemoryMap == (HANDLE)nullptr)
|
|
{
|
|
//error state!
|
|
mInErrorState = true;
|
|
ClearDiskCache();
|
|
return;
|
|
}
|
|
|
|
mMapAddress = MapViewOfFile(mMemoryMap, FILE_MAP_ALL_ACCESS, 0, 0, mCurrentFileMapSize);
|
|
if (mMapAddress == (HANDLE)nullptr)
|
|
{
|
|
//error state!
|
|
mInErrorState = true;
|
|
ClearDiskCache();
|
|
return;
|
|
}
|
|
|
|
mFileStart = (uint8*)mMapAddress;
|
|
}
|
|
|
|
bool FDiskCacheInterface::AppendData(const void* pData, size_t size)
|
|
{
|
|
GrowMapping(size, false);
|
|
if (IsInErrorState())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
memcpy((mFileStart + mCurrentOffset), pData, size);
|
|
mCurrentOffset += size;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FDiskCacheInterface::SetPointerAndAdvanceFilePosition(void** pDest, size_t size, bool backWithSystemMemory)
|
|
{
|
|
GrowMapping(size, false);
|
|
if (IsInErrorState())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Back persistent objects in system memory to avoid
|
|
// troubles when re-mapping the file.
|
|
if (backWithSystemMemory)
|
|
{
|
|
// Optimization: most (all?) of the shader input layout semantic names are "ATTRIBUTE"...
|
|
// instead of making 1000's of attribute strings, just set it to a static one.
|
|
static const char attribute[] = "ATTRIBUTE";
|
|
if (size == sizeof(attribute) && FMemory::Memcmp((mFileStart + mCurrentOffset), attribute, sizeof(attribute)) == 0)
|
|
{
|
|
*pDest = (void*)attribute;
|
|
}
|
|
else
|
|
{
|
|
void* newMemory = FMemory::Malloc(size);
|
|
if (newMemory)
|
|
{
|
|
FMemory::Memcpy(newMemory, (mFileStart + mCurrentOffset), size);
|
|
mBackedMemory.Add(newMemory);
|
|
*pDest = newMemory;
|
|
}
|
|
else
|
|
{
|
|
check(false);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pDest = mFileStart + mCurrentOffset;
|
|
}
|
|
|
|
mCurrentOffset += size;
|
|
|
|
return true;
|
|
}
|
|
|
|
void FDiskCacheInterface::Reset(RESET_TYPE type)
|
|
{
|
|
mCurrentOffset = sizeof(FDiskCacheHeader);
|
|
|
|
if (type == RESET_TO_AFTER_LAST_OBJECT)
|
|
{
|
|
mCurrentOffset += mHeader.mSizeInBytes;
|
|
}
|
|
}
|
|
|
|
void FDiskCacheInterface::Close(uint32 numberOfPSOs)
|
|
{
|
|
mHeader.mNumPsos = numberOfPSOs;
|
|
|
|
check(mCurrentOffset >= sizeof(FDiskCacheHeader));
|
|
mHeader.mSizeInBytes = mCurrentOffset - sizeof(FDiskCacheHeader);
|
|
|
|
if (!IsInErrorState())
|
|
{
|
|
if (mMapAddress)
|
|
{
|
|
*(FDiskCacheHeader*)mFileStart = mHeader;
|
|
FlushViewOfFile(mMapAddress, mCurrentOffset);
|
|
UnmapViewOfFile(mMapAddress);
|
|
}
|
|
if (mMemoryMap)
|
|
{
|
|
CloseHandle(mMemoryMap);
|
|
}
|
|
if (mFile)
|
|
{
|
|
CloseHandle(mFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDiskCacheInterface::ClearDiskCache()
|
|
{
|
|
// Prevent reads/writes
|
|
mInErrorState = true;
|
|
mHeader.mHeaderVersion = mCurrentHeaderVersion;
|
|
mHeader.mNumPsos = 0;
|
|
|
|
if (!mEnableDiskCache)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (mMapAddress)
|
|
{
|
|
UnmapViewOfFile(mMapAddress);
|
|
}
|
|
if (mMemoryMap)
|
|
{
|
|
CloseHandle(mMemoryMap);
|
|
}
|
|
if (mFile)
|
|
{
|
|
CloseHandle(mFile);
|
|
}
|
|
#ifdef UNICODE
|
|
BOOL result = DeleteFileW(mFileName.GetCharArray().GetData());
|
|
#else
|
|
bool result = DeleteFileA(mFileName.GetCharArray().GetData());
|
|
#endif
|
|
UE_LOG(LogD3D12RHI, Warning, TEXT("Deleted PSO Cache with result %d"), result);
|
|
}
|
|
|
|
void FDiskCacheInterface::Flush(uint32 numberOfPSOs)
|
|
{
|
|
mHeader.mNumPsos = numberOfPSOs;
|
|
|
|
check(mCurrentOffset >= sizeof(FDiskCacheHeader));
|
|
mHeader.mSizeInBytes = mCurrentOffset - sizeof(FDiskCacheHeader);
|
|
|
|
if (mMapAddress && !IsInErrorState())
|
|
{
|
|
*(FDiskCacheHeader*)mFileStart = mHeader;
|
|
FlushViewOfFile(mMapAddress, mCurrentOffset);
|
|
}
|
|
}
|
|
|
|
void* FDiskCacheInterface::GetDataAt(SIZE_T Offset) const
|
|
{
|
|
void* data = mFileStart + Offset;
|
|
|
|
check(data <= (mFileStart + mCurrentFileMapSize));
|
|
return data;
|
|
}
|
|
|
|
void* FDiskCacheInterface::GetDataAtStart() const
|
|
{
|
|
return GetDataAt(sizeof(FDiskCacheHeader));
|
|
}
|