Files
FC1/RenderDll/Common/Textures/TexManStreaming.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

1685 lines
46 KiB
C++

/*=============================================================================
TexManStreaming.cpp : Common Texture manager implementation.
Copyright (c) 2001 Crytek Studios. All Rights Reserved.
Revision history:
* Created by Khonich Andrey
=============================================================================*/
#include "RenderPCH.h"
#include "../CommonRender.h"
#include "Image/DDSImage.h"
#include "Image/dds.h"
// DXT compressed block for black image (DXT1, DXT3, DXT5)
static byte sDXTData[3][16] =
{
{0,0,0,0,0,0,0,0},
{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0,0,0,0,0,0,0,0},
{0xff,0xff,0,0,0,0,0,0, 0,0,0,0,0,0,0,0}
};
void STexPic::CreateMips()
{
int nSides = (m_eTT == eTT_Cubemap) ? 6 : 1;
int nMips = m_nMips;
for (int i=0; i<nSides; i++)
{
if (!m_Mips[i])
{
m_Mips[i] = new SMipmap*[nMips];
memset(m_Mips[i], 0, sizeof(SMipmap *)*nMips);
}
}
}
void STexPic::RemoveMips(int nFromEnd)
{
int nSides = (m_eTT == eTT_Cubemap) ? 6 : 1;
int nMips = m_nMips;
bool bRelease;
if (!nFromEnd)
bRelease = true;
else
bRelease = false;
if (nMips <= 1)
nFromEnd = 0;
int nEnd = nMips-nFromEnd;
{
for (int i=0; i<nSides; i++)
{
if (!m_Mips[i])
continue;
for (int j=0; j<nEnd; j++)
{
if (bRelease)
{
SAFE_DELETE(m_Mips[i][j]);
}
else
if (m_Mips[i][j])
m_Mips[i][j]->DataArray.Free();
}
if (bRelease)
SAFE_DELETE_ARRAY(m_Mips[i]);
}
}
}
void STexPic::Unload()
{
if (m_Flags2 & FT2_WASUNLOADED)
return;
if (!IsStreamed())
return;
// Set();
if (gRenDev->m_TexMan->m_Streamed & 1)
{
if (gRenDev->m_LogFileStr)
gRenDev->LogStrv(SRendItem::m_RecurseLevel, "Unload '%s', Time: %.3f\n", m_SourceName.c_str(), iTimer->GetAsyncCurTime());
SaveToCache();
ReleaseDriverTexture();
m_Flags2 |= FT2_WASUNLOADED;
gRenDev->SetTexture(0, eTT_Base);
return;
}
}
void STexPic::Restore()
{
if (gRenDev->m_TexMan->m_Streamed & 1)
{
// Check the distance
float fDist = -1.0f;
if (gRenDev->m_RP.m_pRE)
{
#ifndef PIPE_USE_INSTANCING
fDist = gRenDev->m_RP.m_pRE->mfDistanceToCameraSquared(*gRenDev->m_RP.m_pCurObject);
#else
fDist = 999999.0f;
int nObj = 0;
CCObject *pSaveObj = gRenDev->m_RP.m_pCurObject;
CCObject *pObj = pSaveObj;
while (true)
{
if (nObj)
{
gRenDev->m_RP.m_pCurObject = gRenDev->m_RP.m_MergedObjects[nObj];
gRenDev->m_RP.m_FrameObject++;
pObj = gRenDev->m_RP.m_pCurObject;
}
fDist = min(fDist, gRenDev->m_RP.m_pRE->mfDistanceToCameraSquared(*pObj));
nObj++;
if (nObj >= gRenDev->m_RP.m_MergedObjects.Num())
break;
}
if (pSaveObj != gRenDev->m_RP.m_pCurObject)
{
gRenDev->m_RP.m_pCurObject = pSaveObj;
gRenDev->m_RP.m_FrameObject++;
}
#endif
fDist = crySqrtf(fDist);
if (m_Flags2 & FT2_NEEDTORELOAD)
fDist = min(m_fMinDistance, fDist);
}
//gRenDev->m_TexMan->CheckTexLimits(this);
LoadFromCache(FPR_IMMEDIATELLY, fDist);
gRenDev->SetTexture(0, eTT_Base);
return;
}
}
static char *sETFToStr(ETEX_Format ETF)
{
char *sETF;
switch (ETF)
{
case eTF_Index:
sETF = "Index";
break;
case eTF_0888:
sETF = "0888";
break;
case eTF_8888:
sETF = "8888";
break;
case eTF_8000:
sETF = "8000";
break;
case eTF_0088:
sETF = "0088";
break;
case eTF_DXT1:
sETF = "DXT1";
break;
case eTF_DXT3:
sETF = "DXT3";
break;
case eTF_DXT5:
sETF = "DXT5";
break;
case eTF_SIGNED_HILO16:
sETF = "SIGNED_HILO16";
break;
case eTF_DSDT_MAG:
sETF = "DSDT_MAG";
break;
case eTF_DSDT:
sETF = "DSDT";
break;
case eTF_RGB8:
sETF = "RGB8";
break;
case eTF_RGBA:
sETF = "RGBA";
break;
default:
assert(0);
sETF = "Unknown"; // for better behaviour in non debug
break;
}
return sETF;
}
void STexPic::GetCacheName(char *name)
{
char *sETT;
switch (m_eTT)
{
case eTT_Base:
sETT = "Base";
break;
case eTT_DSDTBump:
sETT = "DSDTBump";
break;
case eTT_Bumpmap:
sETT = "Normalmap";
break;
case eTT_Cubemap:
sETT = "Cubemap";
break;
case eTT_Rectangle:
sETT = "Rectangle";
break;
}
char nam[512];
if (!strncmp("Spr_$", m_SourceName.c_str(), 5))
strcpy(nam, m_SourceName.c_str());
else
StripExtension(m_SourceName.c_str(), nam);
sprintf(name, "%s[%s]", nam, sETT);
}
void STexPic::SaveToCache()
{
int i, j;
if (m_CacheID >= 0 || (m_Flags2 & FT2_STREAMFROMDDS))
return;
CResFile *rf;
rf = gRenDev->m_TexMan->m_TexCache;
SDirEntry de;
char name[512];
GetCacheName(name);
if (!rf)
gRenDev->m_TexMan->CreateCacheFile();
rf = gRenDev->m_TexMan->m_TexCache;
bool bKeepPlace = false;
if (rf->mfFileExist(name)>=0)
{
if (m_Flags2 & FT2_VERSIONWASCHECKED)
return;
m_Flags2 |= FT2_VERSIONWASCHECKED;
if (m_CacheID < 0)
m_CacheID = rf->mfFileGetNum(name);
if (m_Flags2 & FT2_DISCARDINCACHE)
{
STexCacheFileHeader fh;
STexCacheMipHeader mh;
rf->mfFileSeek(m_CacheID, 0, SEEK_SET);
rf->mfFileRead2(m_CacheID, sizeof(STexCacheFileHeader), &fh);
rf->mfFileRead2(m_CacheID, sizeof(STexCacheMipHeader), &mh);
if (m_Width == mh.m_USize && m_Height == mh.m_VSize)
bKeepPlace = true;
}
if (!bKeepPlace)
{
STexCacheFileHeader fh;
rf->mfFileSeek(m_CacheID, 0, SEEK_SET);
rf->mfFileRead2(m_CacheID, sizeof(STexCacheFileHeader), &fh);
if (fh.m_sExt[0] == 0)
{
if (fh.m_nMips == m_nMips)
return;
else
{
rf->mfFileDelete(m_CacheID);
m_CacheID = -1;
}
}
if (m_CacheID >= 0)
{
char nameInRes[512];
char nameFile[2][512];
int nFiles = GetFileNames(nameFile[0], nameFile[1], 128);
int n = 0;
while (name[n] != '[')
{
nameInRes[n] = name[n];
nameInRes[n+1] = 0;
n++;
}
strcat(nameInRes, fh.m_sExt);
if (!stricmp(nameInRes, m_SourceName.c_str()))
{
int nCompares = 0;
for (i=0; i<nFiles; i++)
{
HANDLE status2 = CreateFile(nameFile[i],GENERIC_READ,FILE_SHARE_READ, NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if (status2 != INVALID_HANDLE_VALUE)
{
FILETIME writetime1,writetime2;
writetime1 = fh.m_WriteTime[i];
GetFileTime(status2,NULL,NULL,&writetime2);
CloseHandle(status2);
if (CompareFileTime(&writetime1,&writetime2)==0)
nCompares++;
}
}
if (nCompares == nFiles)
return;
}
rf->mfFileDelete(m_CacheID);
m_CacheID = -1;
}
}
}
STexCacheMipHeader mh;
STexCacheFileHeader fh;
TArray<byte> Data;
memset(&fh, 0, sizeof(fh));
BuildMips();
int nSides;
if (m_Mips[5])
nSides = 6;
else
nSides = 1;
fh.m_SizeOf = sizeof(fh);
fh.m_Version = TEXCACHE_VERSION;
fh.m_nSides = nSides;
fh.m_nMips = m_nMips;
fh.m_DstFormat = m_DstFormat;
fh.m_bPolyBump = false;
fh.m_bCloneSpace = false;
const char *pExt;
if (m_SourceName.c_str()[0] && (pExt=GetExtension(m_SourceName.c_str())))
{
if (strlen(pExt) < 6)
strncpy(fh.m_sExt, pExt, 6);
else
fh.m_sExt[0] = 0;
}
else
fh.m_sExt[0] = 0;
strcpy(fh.m_sETF, sETFToStr(m_ETF));
char nameFile[2][128];
int nFiles = GetFileNames(nameFile[0], nameFile[1], 128);
for (i=0; i<nFiles; i++)
{
HANDLE status = CreateFile(nameFile[i],GENERIC_READ,FILE_SHARE_READ, NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if (status != INVALID_HANDLE_VALUE)
{
FILETIME writetime;
GetFileTime(status,NULL,NULL,&writetime);
CloseHandle(status);
fh.m_WriteTime[i] = writetime;
}
else
{
fh.m_WriteTime[i].dwHighDateTime = 0;
fh.m_WriteTime[i].dwLowDateTime = 0;
}
}
m_Flags2 |= FT2_VERSIONWASCHECKED;
int n = Data.Num();
Data.Grow(sizeof(STexCacheFileHeader));
memcpy(&Data[n], &fh, sizeof(STexCacheFileHeader));
for (i=0; i<m_nMips; i++)
{
SMipmap *mp = m_Mips[0][i];
mh.m_SizeOf = sizeof(mh);
mh.m_USize = mp->USize;
mh.m_VSize = mp->VSize;
mh.m_Size = mp->DataArray.Num();
mh.m_SizeWithMips = 0;
for (j=i; j<m_nMips; j++)
{
mh.m_SizeWithMips += m_Mips[0][j]->DataArray.Num();
}
int n = Data.Num();
Data.Grow(sizeof(STexCacheMipHeader));
memcpy(&Data[n], &mh, sizeof(STexCacheMipHeader));
}
for (j=0; j<nSides; j++)
{
for (i=0; i<m_nMips; i++)
{
SMipmap *mp = m_Mips[j][i];
int n = Data.Num();
Data.Grow(mp->DataArray.Num());
memcpy(&Data[n], &mp->DataArray[0], mp->DataArray.Num());
}
}
SDirEntry *pde = rf->mfGetEntry(m_CacheID);
if (bKeepPlace && Data.Num() == pde->size)
{
iSystem->GetIPak()->FSeek(rf->mfGetHandle(), pde->offset, SEEK_SET);
iSystem->GetIPak()->FWrite(&Data[0], 1, Data.Num(), rf->mfGetHandle());
}
else
{
memset(&de, 0, sizeof(de));
de.ID = CName(name, eFN_Add).GetIndex();
de.size = Data.Num();
de.eid = eRI_BIN;
de.user.data = &Data[0];
de.earc = eARC_NONE;
rf->mfFileAdd(&de);
rf->mfFlush();
}
RemoveMips(4);
}
bool STexPic::ValidateMipsData(int nStartMip, int nEndMip)
{
int i, j;
bool bOk = true;
if (m_eTT == eTT_Cubemap)
{
for (j=0; j<6; j++)
{
for (i=nStartMip; i<=nEndMip; i++)
{
SMipmap *mp = m_Mips[j][i];
if (!CRenderer::CV_r_texturesstreamingonlyvideo)
{
if (mp && mp->m_bUploaded)
continue;
}
if (!mp || !mp->DataArray.GetSize())
{
bOk = false;
break;
}
}
}
}
else
{
for (i=nStartMip; i<=nEndMip; i++)
{
SMipmap *mp = m_Mips[0][i];
if (!CRenderer::CV_r_texturesstreamingonlyvideo)
{
if (mp && mp->m_bUploaded)
continue;
}
if (!mp || !mp->DataArray.GetSize())
{
bOk = false;
break;
}
}
}
return bOk;
}
void CTextureStreamCallback::StreamOnComplete (IReadStream* pStream, unsigned nError)
{
PROFILE_FRAME(Texture_AsyncUpload);
int i, j;
STexCacheInfo *pTexCacheFileInfo = (STexCacheInfo *)pStream->GetUserData();
if (gRenDev->m_TexMan->m_Textures.Num() <= pTexCacheFileInfo->m_TexId)
return;
STexPic *tp = gRenDev->m_TexMan->m_Textures[pTexCacheFileInfo->m_TexId];
if (!tp || !tp->m_pFileTexMips)
{
//Warning( VALIDATOR_FLAG_TEXTURE,tp->GetName(),"CTextureStreamCallback::StreamOnComplete texture is missing %s",tp->GetName() );
return;
}
int n = 0;
byte *Src = (byte *)pStream->GetBuffer();
STexCacheFileHeader *fh = &tp->m_CacheFileHeader;
STexCacheMipHeader *mh = tp->m_pFileTexMips;
int nMips = fh->m_nMips;
int nSide = pTexCacheFileInfo->m_nCubeSide;
int nS = nSide < 0 ? 0 : nSide;
if (gRenDev->m_LogFileStr)
gRenDev->LogStrv(SRendItem::m_RecurseLevel, "Async Finish Load '%s' (Side: %d, %d-%d[%d]), Size: %d, Time: %.3f\n", tp->m_SourceName.c_str(), nS, pTexCacheFileInfo->m_nStartLoadMip, pTexCacheFileInfo->m_nEndLoadMip, tp->m_nMips, pTexCacheFileInfo->m_nSizeToLoad, iTimer->GetAsyncCurTime());
for (i=pTexCacheFileInfo->m_nStartLoadMip; i<=pTexCacheFileInfo->m_nEndLoadMip; i++)
{
SMipmap *mp = tp->m_Mips[nS][i];
assert(mp);
mp->m_bLoading = false;
if (!mp->m_bUploaded || (CRenderer::CV_r_texturesstreamingonlyvideo && !mp->DataArray.GetSize())) // Already uploaded synchronously
{
double time0 = 0;
ticks(time0);
if (!mp->DataArray.GetSize())
mp->DataArray.Alloc(mh[i].m_Size);
if (tp->m_ETF == eTF_0888 && (tp->m_Flags2 & FT2_STREAMFROMDDS))
{
byte *pTemp = new byte[mp->USize*mp->VSize*3];
cryMemcpy(pTemp, &Src[n], mp->USize*mp->VSize*3);
for (int n=0; n<mp->USize*mp->VSize; n++)
{
mp->DataArray[n*4+0] = pTemp[n*3+0];
mp->DataArray[n*4+1] = pTemp[n*3+1];
mp->DataArray[n*4+2] = pTemp[n*3+2];
mp->DataArray[n*4+3] = 255;
}
delete [] pTemp;
}
else
cryMemcpy(&mp->DataArray[0], &Src[n], mh[i].m_Size);
unticks(time0);
gRenDev->m_RP.m_PS.m_fTexUploadTime += (float)(time0*1000.0*g_SecondsPerCycle);
}
if (tp->m_ETF == eTF_0888 && (tp->m_Flags2 & FT2_STREAMFROMDDS))
n += mh[i].m_USize*mh[i].m_VSize*3;
else
n += mh[i].m_Size;
}
gRenDev->m_TexMan->ValidateTexSize();
bool bNeedToWait = false;
if (nSide >= 0)
{
if (tp->m_Flags2 & (FT2_REPLICATETOALLSIDES | FT2_FORCECUBEMAP))
{
for (j=1; j<6; j++)
{
for (int i=pTexCacheFileInfo->m_nStartLoadMip; i<=pTexCacheFileInfo->m_nEndLoadMip; i++)
{
if (!tp->m_Mips[j][i])
tp->m_Mips[j][i] = new SMipmap(mh[i].m_USize, mh[i].m_VSize);
SMipmap *mp = tp->m_Mips[j][i];
if (!mp->DataArray.GetSize())
mp->DataArray.Alloc(mh[i].m_Size);
if (tp->m_Flags2 & FT2_REPLICATETOALLSIDES)
cryMemcpy(&mp->DataArray[0], &tp->m_Mips[0][i]->DataArray[0], mp->DataArray.GetSize());
else
if (tp->m_Flags2 & FT2_FORCECUBEMAP)
{
if (tp->m_ETF == eTF_DXT1 || tp->m_ETF == eTF_DXT3 || tp->m_ETF == eTF_DXT5)
{
int nBlocks = ((mp->USize+3)/4)*((mp->VSize+3)/4);
int blockSize = tp->m_ETF == eTF_DXT1 ? 8 : 16;
int nOffsData = tp->m_ETF - eTF_DXT1;
int nCur = 0;
for (int n=0; n<nBlocks; n++)
{
cryMemcpy(&mp->DataArray[nCur], sDXTData[nOffsData], blockSize);
nCur += blockSize;
}
}
else
memset(&mp->DataArray[0], 0, mp->DataArray.GetSize());
}
}
}
}
for (j=0; j<6; j++)
{
if (!tp->m_Mips[j])
{
bNeedToWait = true;
break;
}
for (i=pTexCacheFileInfo->m_nStartLoadMip; i<=pTexCacheFileInfo->m_nEndLoadMip; i++)
{
SMipmap *mp = tp->m_Mips[j][i];
if (!mp || !mp->DataArray.GetSize())
{
bNeedToWait = true;
break;
}
}
if (i <= pTexCacheFileInfo->m_nEndLoadMip)
break;
}
}
if (!bNeedToWait)
{
bool bOk = tp->ValidateMipsData(pTexCacheFileInfo->m_nStartLoadMip, nMips-1);
if (!bOk)
{
tp->RemoveMips(4);
tp->m_Flags2 |= FT2_WASUNLOADED;
}
else
{
bool bRes = tp->UploadMips(pTexCacheFileInfo->m_nStartLoadMip, nMips-1);
if (!pTexCacheFileInfo->m_nStartLoadMip && bRes)
tp->RemoveMips(4);
tp->m_Flags2 &= ~FT2_WASUNLOADED;
if (!tp->m_Mips[0][0] || !tp->m_Mips[0][0]->m_bUploaded)
tp->m_Flags2 |= FT2_PARTIALLYLOADED;
else
tp->m_Flags2 &= ~FT2_PARTIALLYLOADED;
}
tp->Relink(&STexPic::m_Root);
gRenDev->m_TexMan->CheckTexLimits(NULL);
}
SAFE_DELETE(pTexCacheFileInfo);
tp->m_Flags2 &= ~FT2_STREAMINGINPROGRESS;
gRenDev->m_TexMan->ValidateTexSize();
}
int STexPic::UpdateMip(float fDist)
{
int nMip = 0;
if (fDist)
{
fDist *= gRenDev->m_TexMan->m_fStreamDistFactor;
nMip = min(m_nMips-1, (int)(fDist / (float)CRenderer::CV_r_texturespixelsize));
}
if ((m_Width > 64 || m_Height > 64) && !CRenderer::CV_r_texturesstreamingsync)
{
int nResMip = CRenderer::CV_r_texturesbasemip;
if (m_eTT == eTT_Bumpmap)
nResMip += CRenderer::CV_r_texbumpresolution + m_nCustomMip;
else
nResMip += CRenderer::CV_r_texresolution + m_nCustomMip;
nResMip = min(m_nMips-1, nResMip);
m_nBaseMip = nResMip;
nMip = min(m_nMips-1, nMip+nResMip);
}
else
m_nBaseMip = 0;
return nMip;
}
void STexPic::LoadFromCache(int Flags, float fDist)
{
int i, j;
PROFILE_FRAME(Texture_LoadFromCache);
int nStartMip = 0;
if (fDist >= 0)
{
//float fTSize = (float)Max(m_Width, m_Height);
if (m_nFrameCache != gRenDev->GetFrameID())
{
m_nFrameCache = gRenDev->GetFrameID();
m_fMinDistance = fDist;
}
else
if (fDist < m_fMinDistance)
m_fMinDistance = fDist;
nStartMip = UpdateMip(m_fMinDistance);
}
else
if (m_nFrameCache != gRenDev->GetFrameID())
nStartMip = UpdateMip(0);
else
nStartMip = UpdateMip(m_fMinDistance);
if (m_Mips[0] && m_Mips[0][nStartMip] && m_Mips[0][nStartMip]->m_bUploaded)
{
if ((m_Flags2 & FT2_NEEDTORELOAD))
{
if (m_Mips[0][nStartMip]->DataArray.GetSize())
{
gRenDev->m_TexMan->ValidateTexSize();
//STexPic ttt;
//memcpy (&ttt, this, sizeof(ttt));
//memset (this, &ttt, sizeof(ttt));
if (ValidateMipsData(nStartMip, m_nMips-1))
UploadMips(nStartMip, m_nMips-1);
m_Flags2 &= ~FT2_NEEDTORELOAD;
for (i=0; i<nStartMip; i++)
{
if (m_Mips[0][i])
m_Mips[0][i]->m_bUploaded = false;
}
if (!m_Mips[0][0] || !m_Mips[0][0]->m_bUploaded)
m_Flags2 |= FT2_PARTIALLYLOADED;
else
m_Flags2 &= ~FT2_PARTIALLYLOADED;
gRenDev->m_TexMan->ValidateTexSize();
return;
}
else
{
for (i=0; i<m_nMips; i++)
{
if (m_Mips[0][i])
m_Mips[0][i]->m_bUploaded = false;
}
}
}
else
return;
}
gRenDev->m_TexMan->ValidateTexSize();
m_Flags2 &= ~FT2_NEEDTORELOAD;
CResFile *rf;
rf = gRenDev->m_TexMan->m_TexCache;
int nMips, nSides;
if (!m_pFileTexMips)
{
if (m_CacheID < 0)
{
char name[512];
GetCacheName(name);
if (rf->mfFileExist(name)<0)
return; // Should never happen
m_CacheID = rf->mfFileGetNum(name);
}
assert(m_CacheID >= 0);
rf->mfFileSeek(m_CacheID, 0, SEEK_SET);
rf->mfFileRead2(m_CacheID, sizeof(STexCacheFileHeader), &m_CacheFileHeader);
nSides = m_CacheFileHeader.m_nSides;
nMips = m_CacheFileHeader.m_nMips;
m_pFileTexMips = new STexCacheMipHeader[nMips];
rf->mfFileRead2(m_CacheID, sizeof(STexCacheMipHeader)*nMips, m_pFileTexMips);
}
STexCacheFileHeader *fh = &m_CacheFileHeader;
STexCacheMipHeader *mh = m_pFileTexMips;
nSides = fh->m_nSides;
nMips = fh->m_nMips;
assert(nMips == m_nMips);
if (!m_Mips[0])
CreateMips();
int Size;
int nEndMip = m_nMips-1;
int n = nEndMip;
int nSyncStartMip = -1;
int nSyncEndMip = -1;
int nASyncStartMip = -1;
int nASyncEndMip = -1;
if (nMips == 1 || CRenderer::CV_r_texturesstreamingsync)
{
nSyncStartMip = nStartMip;
nSyncEndMip = nEndMip;
}
else
{
// Always stream lowest 4 mips synchronously
int nStartLowestM = max(nStartMip, nEndMip-3);
for (i=nStartLowestM; i<=nEndMip; i++)
{
if (!m_Mips[0][i] || !m_Mips[0][i]->m_bUploaded)
{
nSyncStartMip = i;
for (j=i+1; j<=nEndMip; j++)
{
if (m_Mips[0][j] && m_Mips[0][j]->m_bUploaded)
break;
}
nSyncEndMip = j-1;
break;
}
}
// Let's see which part of the texture not loaded yet and stream it asynhronously
if (nStartLowestM > nStartMip)
{
for (i=nStartMip; i<=nStartLowestM-1; i++)
{
if (!m_Mips[0][i] || (!m_Mips[0][i]->m_bLoading && !m_Mips[0][i]->m_bUploaded))
{
nASyncStartMip = i;
break;
}
}
if (nASyncStartMip >= 0 && nASyncStartMip <= nStartLowestM-1)
{
for (i=nASyncStartMip; i<=nStartLowestM-1; i++)
{
if (m_Mips[0][i] && (m_Mips[0][i]->m_bUploaded || m_Mips[0][i]->m_bLoading))
break;
}
nASyncEndMip = i-1;
}
}
}
if (nASyncStartMip < 0 && nSyncStartMip < 0)
return;
if (!(Flags & FPR_IMMEDIATELLY))
{
int nSize = 0;
for (i=nSyncStartMip; i<=nSyncEndMip; i++)
{
nSize += mh[i].m_Size;
}
for (i=nASyncStartMip; i<=nASyncEndMip; i++)
{
nSize += mh[i].m_Size;
}
int n10Perc = CRenderer::CV_r_texturesstreampoolsize*1024*1024/100*10;
if (gRenDev->m_TexMan->m_StatsCurTexMem+nSize+n10Perc >= CRenderer::CV_r_texturesstreampoolsize*1024*1024)
return;
}
Relink(&STexPic::m_Root);
IStreamEngine *pSE = iSystem->GetStreamEngine();
FILE *fp = NULL;
// Synchronous loading
static char* cubefaces[6] = {"posx","negx","posy","negy","posz","negz"};
if (nSyncStartMip >= 0)
{
PROFILE_FRAME(Texture_LoadFromCacheSync);
assert(nSyncStartMip <= nSyncEndMip);
int nSeekFromStart = 0;
for (i=0; i<nSyncStartMip; i++)
{
if (m_ETF == eTF_0888 && (m_Flags2 & FT2_STREAMFROMDDS))
nSeekFromStart += mh[i].m_USize*mh[i].m_VSize*3;
else
nSeekFromStart += mh[i].m_Size;
}
if (nSides > 1)
Size = m_Size/6;
else
Size = m_Size;
for (j=0; j<nSides; j++)
{
if (m_CacheID < 0)
{
if (j)
{
if (fp)
iSystem->GetIPak()->FClose(fp);
char name[512];
StripExtension(m_SourceName.c_str(), name);
size_t len = strlen(name);
if (len > 5)
{
for (int i=0; i<6; i++)
{
if (!stricmp(&name[len-4], cubefaces[i]))
{
if (name[len-5] == '_')
len--;
name[len-4] = 0;
break;
}
}
}
char cube[512];
sprintf(cube, "%s_%s.dds", name, cubefaces[j]);
fp = iSystem->GetIPak()->FOpen(cube, "rb");
}
if (!j || !(m_Flags2 & FT2_FORCECUBEMAP | FT2_REPLICATETOALLSIDES))
{
if (!fp)
fp = iSystem->GetIPak()->FOpen(m_SourceName.c_str(), "rb");
}
if (fp)
iSystem->GetIPak()->FSeek(fp, sizeof(DWORD)+sizeof(DDS_HEADER)+nSeekFromStart, SEEK_SET);
}
else
rf->mfFileSeek(m_CacheID, sizeof(STexCacheFileHeader)+nMips*sizeof(STexCacheMipHeader)+Size*j+nSeekFromStart, SEEK_SET);
int SizeToLoad = 0;
for (i=nSyncStartMip; i<=nSyncEndMip; i++)
{
if (m_Mips[j][i] && (m_Mips[j][i]->DataArray.GetSize() || m_Mips[j][i]->m_bUploaded))
{
i++;
break;
}
SMipmap *mp;
if (!m_Mips[j][i])
m_Mips[j][i] = new SMipmap(mh[i].m_USize, mh[i].m_VSize);
mp = m_Mips[j][i];
if (!mp->DataArray.GetSize())
mp->DataArray.Alloc(mh[i].m_Size);
assert(!mp->m_bUploaded);
gRenDev->m_TexMan->m_LoadBytes += mh[i].m_Size;
SizeToLoad += mh[i].m_Size;
if (fp)
{
if (m_ETF == eTF_0888 && (m_Flags2 & FT2_STREAMFROMDDS))
{
byte *pTemp = new byte[mp->USize*mp->VSize*3];
iSystem->GetIPak()->FRead(pTemp, mp->USize*mp->VSize*3, 1, fp);
for (int n=0; n<mp->USize*mp->VSize; n++)
{
mp->DataArray[n*4+0] = pTemp[n*3+0];
mp->DataArray[n*4+1] = pTemp[n*3+1];
mp->DataArray[n*4+2] = pTemp[n*3+2];
mp->DataArray[n*4+3] = 255;
}
delete [] pTemp;
}
else
if (fp)
iSystem->GetIPak()->FRead(&mp->DataArray[0], mh[i].m_Size, 1, fp);
else
if (j)
{
if (m_Flags2 & FT2_REPLICATETOALLSIDES)
memcpy(&mp->DataArray[0], &m_Mips[0][i]->DataArray[0], mp->DataArray.GetSize());
else
if (m_Flags2 & FT2_FORCECUBEMAP)
{
if (m_ETF == eTF_DXT1 || m_ETF == eTF_DXT3 || m_ETF == eTF_DXT5)
{
int nBlocks = ((mp->USize+3)/4)*((mp->VSize+3)/4);
int blockSize = m_ETF == eTF_DXT1 ? 8 : 16;
int nOffsData = m_ETF - eTF_DXT1;
int nCur = 0;
for (int n=0; n<nBlocks; n++)
{
memcpy(&mp->DataArray[nCur], sDXTData[nOffsData], blockSize);
nCur += blockSize;
}
}
else
memset(&mp->DataArray[0], 0, mp->DataArray.GetSize());
}
}
}
else
if (m_CacheID >= 0)
rf->mfFileRead2(m_CacheID, mh[i].m_Size, &mp->DataArray[0]);
}
if (gRenDev->m_LogFileStr && SizeToLoad > 0)
gRenDev->LogStrv(SRendItem::m_RecurseLevel, "Sync Load '%s' (Side: %d, %d-%d[%d]), Size: %d, Time: %.3f\n", m_SourceName.c_str(), j, nSyncStartMip, i-1, nMips, SizeToLoad, iTimer->GetAsyncCurTime());
}
if (fp)
iSystem->GetIPak()->FClose(fp);
bool bRes;
{
PROFILE_FRAME(Texture_LoadFromCache_UploadSync);
bRes = UploadMips(nSyncStartMip, nMips-1);
}
if (!nSyncStartMip && bRes)
RemoveMips(4);
m_Flags2 &= ~FT2_WASUNLOADED;
if (!m_Mips[0][0] || !m_Mips[0][0]->m_bUploaded)
m_Flags2 |= FT2_PARTIALLYLOADED;
else
m_Flags2 &= ~FT2_PARTIALLYLOADED;
//gRenDev->m_TexMan->CheckTexLimits(this);
}
// Asynchronous loading
if (nASyncStartMip >= 0)
{
PROFILE_FRAME(Texture_LoadFromCacheASync);
assert(nASyncStartMip <= nASyncEndMip);
int nSeekFromStart = 0;
for (i=0; i<nASyncStartMip; i++)
{
if (m_ETF == eTF_0888 && (m_Flags2 & FT2_STREAMFROMDDS))
nSeekFromStart += mh[i].m_USize*mh[i].m_VSize*3;
else
nSeekFromStart += mh[i].m_Size;
}
if (nSides > 1)
Size = m_Size/6;
else
Size = m_Size;
for (j=0; j<nSides; j++)
{
int SizeToLoad = 0;
for (i=nASyncStartMip; i<=nASyncEndMip; i++)
{
if (m_Mips[j][i] && (m_Mips[j][i]->m_bLoading || m_Mips[j][i]->m_bUploaded))
break;
if (!m_Mips[j][i])
m_Mips[j][i] = new SMipmap(mh[i].m_USize, mh[i].m_VSize);
m_Mips[j][i]->m_bLoading = true;
if (m_ETF == eTF_0888 && (m_Flags2 & FT2_STREAMFROMDDS))
SizeToLoad += mh[i].m_USize*mh[i].m_VSize*3;
else
SizeToLoad += mh[i].m_Size;
}
if (j && (m_Flags2 & (FT2_FORCECUBEMAP | FT2_REPLICATETOALLSIDES)))
continue;
if (SizeToLoad)
{
i--;
if (gRenDev->m_LogFileStr)
gRenDev->LogStrv(SRendItem::m_RecurseLevel, "Async Start Load '%s' (Side: %d, %d-%d[%d]), Size: %d, Time: %.3f\n", m_SourceName.c_str(), j, nASyncStartMip, i, nMips, SizeToLoad, iTimer->GetAsyncCurTime());
gRenDev->m_TexMan->m_LoadBytes += SizeToLoad;
STexCacheInfo *pTexCacheFileInfo = new STexCacheInfo;
pTexCacheFileInfo->m_TexId = m_Id;
pTexCacheFileInfo->m_pTempBufferToStream = new byte[SizeToLoad];
byte *buf = (byte *)pTexCacheFileInfo->m_pTempBufferToStream;
pTexCacheFileInfo->m_nStartLoadMip = nASyncStartMip;
pTexCacheFileInfo->m_nEndLoadMip = i;
pTexCacheFileInfo->m_nSizeToLoad = SizeToLoad;
pTexCacheFileInfo->m_nCubeSide = nSides > 1 ? j : -1;
pTexCacheFileInfo->m_fStartTime = iTimer->GetCurrTime();
m_Flags2 |= FT2_STREAMINGINPROGRESS;
StreamReadParams StrParams;
if (m_CacheID >= 0)
{
SDirEntry *de = rf->mfGetEntry(m_CacheID);
StrParams.nOffset = de->offset+sizeof(STexCacheFileHeader)+nMips*sizeof(STexCacheMipHeader)+Size*j+nSeekFromStart;
}
else
StrParams.nOffset = sizeof(DWORD)+sizeof(DDS_HEADER)+nSeekFromStart;
StrParams.dwUserData = (DWORD_PTR)pTexCacheFileInfo;
StrParams.nLoadTime = 1;
StrParams.nMaxLoadTime = 4;
StrParams.nPriority = 0;
StrParams.pBuffer = pTexCacheFileInfo->m_pTempBufferToStream;
StrParams.nSize = SizeToLoad;
if (m_CacheID >= 0)
{
StrParams.nFlags |= SRP_FLAGS_MAKE_PERMANENT;
IReadStreamPtr pStream = pSE->StartRead(CName(m_CacheID).c_str(), rf->mfGetFileName(), &pTexCacheFileInfo->m_Callback, &StrParams);
}
else
{
if (!j)
IReadStreamPtr pStream = pSE->StartRead(m_SourceName.c_str(), m_SourceName.c_str(), &pTexCacheFileInfo->m_Callback, &StrParams);
else
{
char name[512];
StripExtension(m_SourceName.c_str(), name);
size_t len = strlen(name);
if (len > 5)
{
for (int i=0; i<6; i++)
{
if (!stricmp(&name[len-4], cubefaces[i]))
{
if (name[len-5] == '_')
len--;
name[len-4] = 0;
break;
}
}
}
char cube[512];
sprintf(cube, "%s_%s.dds", name, cubefaces[j]);
IReadStreamPtr pStream = pSE->StartRead(cube, cube, &pTexCacheFileInfo->m_Callback, &StrParams);
}
}
}
}
}
}
static ETEX_Format sStrToETF(const char *sETF)
{
if (!stricmp(sETF, "Index"))
return eTF_Index;
if (!stricmp(sETF, "0888"))
return eTF_0888;
if (!stricmp(sETF, "RGB8"))
return eTF_RGB8;
if (!stricmp(sETF, "8888"))
return eTF_8888;
if (!stricmp(sETF, "8000"))
return eTF_8000;
if (!stricmp(sETF, "0088"))
return eTF_0088;
if (!stricmp(sETF, "DXT1"))
return eTF_DXT1;
if (!stricmp(sETF, "DXT3"))
return eTF_DXT3;
if (!stricmp(sETF, "DXT5"))
return eTF_DXT5;
if (!stricmp(sETF, "SIGNED_HILO16"))
return eTF_SIGNED_HILO16;
if (!stricmp(sETF, "DSDT_MAG"))
return eTF_DSDT_MAG;
if (!stricmp(sETF, "DSDT"))
return eTF_DSDT;
assert (0);
return eTF_8888;
}
static ETexType sStrToETT(const char *sETT)
{
if (!stricmp(sETT, "Base"))
return eTT_Base;
if (!stricmp(sETT, "Rectangle"))
return eTT_Rectangle;
if (!stricmp(sETT, "Cubemap"))
return eTT_Cubemap;
assert(0);
return eTT_Base;
}
#ifdef WIN64
#pragma warning( push ) //AMD Port
#pragma warning( disable : 4267 ) // conversion from 'size_t' to 'int', possible loss of data
#endif
STexPic *CTexMan::LoadFromCache(STexPic *ti, int flags, int flags2, char *name, const char *szModelName, ETexType eTT)
{
if (!(m_Streamed & 1))
return NULL;
if (flags & FT_NOSTREAM)
return NULL;
int i, n;
FILE *fp = NULL;
CResFile *rf = gRenDev->m_TexMan->m_TexCache;
char szName[512];
if (ti && !(flags2 & FT2_DISCARDINCACHE))
{
strcpy(szName, ti->m_SourceName.c_str());
StripExtension(szName, szName);
AddExtension(szName, ".dds");
fp = iSystem->GetIPak()->FOpen(szName, "rb");
if (ti->m_eTT == eTT_Bumpmap && !strstr(ti->m_SourceName.c_str(), "_ddn"))
{
if (fp)
{
iSystem->GetIPak()->FClose(fp);
fp = NULL;
}
iLog->Log("Warning: streaming of heightmap texture %s.dds are not supported (texture name should contain _ddn substring)", ti->m_SourceName.c_str());
}
if (ti->m_eTT == eTT_DSDTBump && !strstr(ti->m_SourceName.c_str(), "_ddt"))
{
if (fp)
{
iSystem->GetIPak()->FClose(fp);
fp = NULL;
}
iLog->Log("Warning: streaming of heightmap texture %s.dds are not supported (texture name should contain _ddt substring)", ti->m_SourceName.c_str());
}
}
if (!fp && !rf)
{
if (!CreateCacheFile())
return NULL;
rf = gRenDev->m_TexMan->m_TexCache;
}
bool bSprite = false;
if (ti && !(flags2 & FT2_DISCARDINCACHE))
ti->GetCacheName(szName);
else
if (!ti)
sprintf(szName, "%s[Base]", name);
else
{
strcpy(szName, ti->m_Name.c_str());
szModelName = &ti->m_SourceName.c_str()[0];
}
ConvertDOSToUnixName(szName, szName);
name = szName;
if (eTT == eTT_Cubemap && m_LastCMStreamed)
{
char SourceName[512];
n = 0;
while (name[n] != '[')
{
SourceName[n] = name[n];
n++;
SourceName[n] = 0;
}
strcat(SourceName, m_LastCMStreamed->m_CacheFileHeader.m_sExt);
ti->m_Width = m_LastCMStreamed->m_Width;
ti->m_Height = m_LastCMStreamed->m_Height;
ti->m_SourceName = SourceName;
ti->m_nMips = m_LastCMStreamed->m_nMips;
ti->m_Size = 0;
if (ti->m_nMips <= 1)
ti->m_Flags |= FT_NOMIPS;
ti->m_eTT = eTT;
int CubeSide;
int tgt = TEXTGT_CUBEMAP;
n = strlen(ti->m_SearchName.c_str()) - 4;
if (!strcmp(&ti->m_SearchName.c_str()[n], "posx"))
CubeSide = 0;
else
if (!strcmp(&ti->m_SearchName.c_str()[n], "negx"))
CubeSide = 1;
else
if (!strcmp(&ti->m_SearchName.c_str()[n], "posy"))
CubeSide = 2;
else
if (!strcmp(&ti->m_SearchName.c_str()[n], "negy"))
CubeSide = 3;
else
if (!strcmp(&ti->m_SearchName.c_str()[n], "posz"))
CubeSide = 4;
else
if (!strcmp(&ti->m_SearchName.c_str()[n], "negz"))
CubeSide = 5;
else
CubeSide = 0;
if (m_LastCMStreamed->m_CubeSide >= CubeSide)
{
m_LastCMStreamed = NULL;
return NULL;
}
ti->m_DstFormat = m_LastCMStreamed->m_DstFormat;
if (CubeSide == 5)
m_LastCMStreamed = NULL;
ti->m_CubeSide = CubeSide;
ti->m_Flags2 |= FT2_WASUNLOADED;
ti->m_Bind = TX_FIRSTBIND + ti->m_Id;
return ti;
}
if (!fp && rf->mfFileExist(name)<0)
return NULL;
if (!ti)
{
bSprite = true;
ti = TextureInfoForName(name, -1, eTT_Base, flags, flags2, -1);
if (ti->m_bBusy)
return ti;
ti->m_bBusy = true;
ti->m_Flags = flags;
ti->m_Flags2 = flags2 & ~FT2_RELOAD;
ti->m_Bind = TX_FIRSTBIND + ti->m_Id;
}
else
if (flags2 & FT2_DISCARDINCACHE)
bSprite = true;
STexCacheFileHeader fh;
int nSides, nMips;
if (fp)
{
iSystem->GetIPak()->FSeek(fp, 0, SEEK_END);
int size = iSystem->GetIPak()->FTell(fp);
iSystem->GetIPak()->FSeek(fp, 0, SEEK_SET);
byte *buf = new byte [size+1];
size = iSystem->GetIPak()->FRead(buf, 1, size, fp);
iSystem->GetIPak()->FClose(fp);
CImageFile::m_CurFileName[0] = 0;
CImageDDSFile im(buf, size);
delete [] buf;
if (im.mfGet_error() == eIFE_OK && im.mfGetFormat() != eIF_Unknown)
{
ti->m_Flags2 |= FT2_STREAMFROMDDS;
int i, j;
fh.m_SizeOf = sizeof(STexCacheFileHeader);
fh.m_bPolyBump = false;
fh.m_bCloneSpace = false;
fh.m_nMips = im.mfGet_numMips();
ETEX_Format eTF;
if (ti->m_eTT == eTT_DSDTBump)
eTF = eTF_DSDT_MAG;
else
eTF = sImageFormat2TexFormat(im.mfGetFormat());
strcpy(fh.m_sETF, sETFToStr(eTF));
strcpy(fh.m_sExt, ".dds");
fh.m_nSides = ti->m_eTT == eTT_Cubemap ? 6 : 1;
fh.m_DstFormat = ti->DstFormatFromTexFormat(eTF);
memcpy(&ti->m_CacheFileHeader, &fh, sizeof(fh));
nSides = ti->m_CacheFileHeader.m_nSides;
nMips = ti->m_CacheFileHeader.m_nMips;
ti->m_pFileTexMips = new STexCacheMipHeader[nMips];
int wdt = im.mfGet_width();
int hgt = im.mfGet_height();
for (i=0; i<nMips; i++)
{
assert(wdt || hgt);
if (!wdt)
wdt = 1;
if (!hgt)
hgt = 1;
ti->m_pFileTexMips[i].m_USize = wdt;
ti->m_pFileTexMips[i].m_VSize = hgt;
if (eTF == eTF_DXT1 || eTF == eTF_DXT3 || eTF == eTF_DXT5)
{
ti->m_pFileTexMips[i].m_USize = (ti->m_pFileTexMips[i].m_USize + 3) & ~3;
ti->m_pFileTexMips[i].m_VSize = (ti->m_pFileTexMips[i].m_VSize + 3) & ~3;
}
ti->m_pFileTexMips[i].m_Size = ti->TexSize(wdt, hgt, fh.m_DstFormat);
ti->m_pFileTexMips[i].m_SizeOf = sizeof(STexCacheMipHeader);
wdt >>= 1;
hgt >>= 1;
}
for (i=0; i<nMips; i++)
{
ti->m_pFileTexMips[i].m_SizeWithMips = 0;
for (j=i; j<nMips; j++)
{
ti->m_pFileTexMips[i].m_SizeWithMips += ti->m_pFileTexMips[j].m_Size;
}
}
}
else
fp = NULL;
}
if (!fp)
{
if (ti->m_CacheID < 0)
ti->m_CacheID = rf->mfFileGetNum(name);
rf->mfFileSeek(ti->m_CacheID, 0, SEEK_SET);
rf->mfFileRead2(ti->m_CacheID, sizeof(STexCacheFileHeader), &fh);
if (fh.m_sExt[0] == 0 && !bSprite)
return NULL;
memcpy(&ti->m_CacheFileHeader, &fh, sizeof(fh));
nSides = ti->m_CacheFileHeader.m_nSides;
nMips = ti->m_CacheFileHeader.m_nMips;
ti->m_pFileTexMips = new STexCacheMipHeader[nMips];
rf->mfFileRead2(ti->m_CacheID, sizeof(STexCacheMipHeader)*nMips, ti->m_pFileTexMips);
}
char SourceName[512];
n = 0;
char sETT[32];
if (bSprite)
{
strcpy(SourceName, szModelName);
ti->m_ETF = eTF_8888;
}
else
{
while (name[n] != '[')
{
SourceName[n] = name[n];
n++;
SourceName[n] = 0;
}
n++;
int m = 0;
while (name[n] != ']')
{
sETT[m] = name[n];
n++;
m++;
}
sETT[m] = 0;
strcat(SourceName, fh.m_sExt);
ti->m_ETF = sStrToETF(fh.m_sETF);
if (ti->m_eTT == eTT_DSDTBump)
ti->m_ETF = eTF_0888;
}
char nameFile[2][128];
int nFiles = ti->GetFileNames(nameFile[0], nameFile[1], 128);
if (nFiles > 1)
strcat(nameFile[1], fh.m_sExt);
else
{
StripExtension(nameFile[0], nameFile[0]);
strcat(nameFile[0], fh.m_sExt);
}
if (!bSprite && !fp)
{
int nComp = 0;
for (i=0; i<nFiles; i++)
{
HANDLE status2 = CreateFile(nameFile[i],GENERIC_READ,FILE_SHARE_READ, NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if (status2 != INVALID_HANDLE_VALUE)
{
FILETIME writetime1,writetime2;
writetime1 = fh.m_WriteTime[i];
GetFileTime(status2,NULL,NULL,&writetime2);
if (CompareFileTime(&writetime1,&writetime2)==0)
nComp++;
CloseHandle(status2);
}
}
if (nComp != nFiles)
{
ti->m_CacheID = -1;
return NULL;
}
}
else
if (bSprite && !(flags2 & FT2_RELOAD))
{
HANDLE status2 = CreateFile(szModelName,GENERIC_READ,FILE_SHARE_READ, NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if (status2 != INVALID_HANDLE_VALUE)
{
FILETIME writetime1,writetime2;
writetime1 = fh.m_WriteTime[0];
GetFileTime(status2,NULL,NULL,&writetime2);
CloseHandle(status2);
if (CompareFileTime(&writetime1,&writetime2)!=0)
{
ti->m_CacheID = -1;
return NULL;
}
}
}
STexCacheMipHeader *mh = ti->m_pFileTexMips;
ti->m_eTT = eTT;
ti->m_Width = ti->m_pFileTexMips[0].m_USize;
ti->m_Height = ti->m_pFileTexMips[0].m_VSize;
ti->m_SourceName = SourceName;
ti->m_nMips = nMips;
ti->m_Size = ti->m_pFileTexMips[0].m_SizeWithMips * nSides;
if (ti->m_nMips <= 1)
ti->m_Flags |= FT_NOMIPS;
int CubeSide = -1;
int tgt = TEXTGT_2D;
if (ti->m_eTT == eTT_Cubemap)
{
tgt = TEXTGT_CUBEMAP;
int n = strlen(ti->m_SearchName.c_str()) - 4;
assert (n > 0);
if (!strcmp(&ti->m_SearchName.c_str()[n], "posx"))
CubeSide = 0;
else
if (!strcmp(&ti->m_SearchName.c_str()[n], "negx"))
CubeSide = 1;
else
if (!strcmp(&ti->m_SearchName.c_str()[n], "posy"))
CubeSide = 2;
else
if (!strcmp(&ti->m_SearchName.c_str()[n], "negy"))
CubeSide = 3;
else
if (!strcmp(&ti->m_SearchName.c_str()[n], "posz"))
CubeSide = 4;
else
if (!strcmp(&ti->m_SearchName.c_str()[n], "negz"))
CubeSide = 5;
else
CubeSide = 0;
}
if (CubeSide < 0 || CubeSide == 5)
m_LastCMStreamed = NULL;
else
m_LastCMStreamed = ti;
ti->m_CubeSide = CubeSide;
ti->Unlink();
if (ti->m_eTT != eTT_Cubemap || !ti->m_CubeSide)
ti->m_Flags2 |= FT2_WASUNLOADED;
ti->m_Flags2 |= FT2_WASLOADED;
ti->m_Bind = TX_FIRSTBIND + ti->m_Id;
AddToHash(ti->m_Bind, ti);
ti->m_RefTex.m_MipFilter = nMips > 1 ? m_MipFilter : 0;
ti->m_DstFormat = fh.m_DstFormat;
if (ti->m_eTT == eTT_Cubemap)
{
ti->m_RefTex.bRepeats = false;
ti->m_RefTex.m_MinFilter = GetMinFilter();
ti->m_RefTex.m_MagFilter = GetMagFilter();
ti->m_RefTex.m_AnisLevel = 1;
}
else
{
if (ti->m_Flags & FT_CLAMP)
ti->m_RefTex.bRepeats = false;
else
ti->m_RefTex.bRepeats = true;
ti->m_RefTex.m_MinFilter = GetMinFilter();
ti->m_RefTex.m_MagFilter = GetMagFilter();
ti->m_RefTex.m_AnisLevel = 1;
}
ti->m_RefTex.m_Type = tgt;
ti->m_RefTex.bProjected = (ti->m_Flags & FT_PROJECTED) ? true : false;
// Always load lowest 4 mips synchronously
fp = NULL;
if (ti->m_nMips > 1)
{
int i, j;
int Size;
if (!ti->m_Mips[0])
ti->CreateMips();
STexCacheFileHeader *fh = &ti->m_CacheFileHeader;
STexCacheMipHeader *mh = ti->m_pFileTexMips;
nSides = fh->m_nSides;
nMips = fh->m_nMips;
int nSyncStartMip = -1;
int nSyncEndMip = -1;
int nEndMip = ti->m_nMips-1;
int nStartLowestM = max(0, nEndMip-3);
for (i=nStartLowestM; i<=nEndMip; i++)
{
if (!ti->m_Mips[0][i] || !ti->m_Mips[0][i]->m_bUploaded)
{
nSyncStartMip = i;
for (j=i+1; j<=nEndMip; j++)
{
if (ti->m_Mips[0][j] && ti->m_Mips[0][j]->m_bUploaded)
break;
}
nSyncEndMip = j-1;
break;
}
}
if (nSyncStartMip >= 0)
{
assert(nSyncStartMip <= nSyncEndMip);
int nSeekFromStart = 0;
for (i=0; i<nSyncStartMip; i++)
{
if (ti->m_ETF == eTF_0888 && (ti->m_Flags2 & FT2_STREAMFROMDDS))
nSeekFromStart += mh[i].m_USize*mh[i].m_VSize*3;
else
nSeekFromStart += mh[i].m_Size;
}
if (nSides > 1)
Size = ti->m_Size/6;
else
Size = ti->m_Size;
static char* cubefaces[6] = {"posx","negx","posy","negy","posz","negz"};
for (j=0; j<nSides; j++)
{
if (ti->m_CacheID < 0)
{
if (j)
{
if (fp)
iSystem->GetIPak()->FClose(fp);
char name[512];
StripExtension(ti->m_SourceName.c_str(), name);
int len = strlen(name);
if (len > 5)
{
for (int i=0; i<6; i++)
{
if (!stricmp(&name[len-4], cubefaces[i]))
{
if (name[len-5] == '_')
len--;
name[len-4] = 0;
break;
}
}
}
char cube[512];
sprintf(cube, "%s_%s.dds", name, cubefaces[j]);
fp = iSystem->GetIPak()->FOpen(cube, "rb");
}
if (!fp)
{
if (!j)
fp = iSystem->GetIPak()->FOpen(ti->m_SourceName.c_str(), "rb");
}
if (fp)
iSystem->GetIPak()->FSeek(fp, sizeof(DWORD)+sizeof(DDS_HEADER)+nSeekFromStart, SEEK_SET);
}
else
rf->mfFileSeek(ti->m_CacheID, sizeof(STexCacheFileHeader)+nMips*sizeof(STexCacheMipHeader)+Size*j+nSeekFromStart, SEEK_SET);
int SizeToLoad = 0;
for (i=nSyncStartMip; i<=nSyncEndMip; i++)
{
if (ti->m_Mips[j][i] && (ti->m_Mips[j][i]->DataArray.GetSize() || ti->m_Mips[j][i]->m_bUploaded))
{
i++;
break;
}
SMipmap *mp;
if (!ti->m_Mips[j][i])
ti->m_Mips[j][i] = new SMipmap(mh[i].m_USize, mh[i].m_VSize);
mp = ti->m_Mips[j][i];
if (!mp->DataArray.GetSize())
mp->DataArray.Alloc(mh[i].m_Size);
assert(!mp->m_bUploaded);
gRenDev->m_TexMan->m_LoadBytes += mh[i].m_Size;
SizeToLoad += mh[i].m_Size;
if (fp)
{
if (ti->m_ETF == eTF_0888)
{
byte *pTemp = new byte[mh[i].m_USize*mh[i].m_VSize*3];
iSystem->GetIPak()->FRead(pTemp, mh[i].m_USize*mh[i].m_VSize*3, 1, fp);
for (int n=0; n<mh[i].m_USize*mh[i].m_VSize; n++)
{
mp->DataArray[n*4+0] = pTemp[n*3+0];
mp->DataArray[n*4+1] = pTemp[n*3+1];
mp->DataArray[n*4+2] = pTemp[n*3+2];
mp->DataArray[n*4+3] = 255;
}
delete [] pTemp;
}
else
iSystem->GetIPak()->FRead(&mp->DataArray[0], mh[i].m_Size, 1, fp);
}
else
if (ti->m_CacheID >= 0)
rf->mfFileRead2(ti->m_CacheID, mh[i].m_Size, &mp->DataArray[0]);
else
if (j)
{
if (ti->m_Flags2 & FT2_REPLICATETOALLSIDES)
memcpy(&mp->DataArray[0], &ti->m_Mips[0][i]->DataArray[0], mp->DataArray.GetSize());
else
if (ti->m_Flags2 & FT2_FORCECUBEMAP)
{
if (ti->m_ETF == eTF_DXT1 || ti->m_ETF == eTF_DXT3 || ti->m_ETF == eTF_DXT5)
{
int nBlocks = ((mp->USize+3)/4)*((mp->VSize+3)/4);
int blockSize = ti->m_ETF == eTF_DXT1 ? 8 : 16;
int nOffsData = ti->m_ETF - eTF_DXT1;
int nCur = 0;
for (int n=0; n<nBlocks; n++)
{
memcpy(&mp->DataArray[nCur], sDXTData[nOffsData], blockSize);
nCur += blockSize;
}
}
else
memset(&mp->DataArray[0], 0, mp->DataArray.GetSize());
}
}
}
}
if (fp)
{
iSystem->GetIPak()->FClose(fp);
fp = NULL;
}
}
}
return ti;
}
#ifdef WIN64
#pragma warning( pop ) //AMD Port
#endif
//=========================================================================
bool CTexMan::CreateCacheFile()
{
STexCacheFileHeader fh;
STexCacheMipHeader mh;
bool bValid = true;
#ifdef DIRECT3D8
CResFile *rf = new CResFile("Textures\\TexturesD3D8.cache", eFSD_name);
#elif DIRECT3D9
CResFile *rf = new CResFile("Textures\\TexturesD3D9.cache", eFSD_name);
#elif OPENGL
CResFile *rf = new CResFile("Textures\\TexturesOGL.cache", eFSD_name);
#else
CResFile *rf = new CResFile("Textures\\Textures.cache", eFSD_name);
#endif
rf->mfOpen(RA_READ);
if (rf->mfGetError())
{
rf->mfClose();
rf->mfOpen(RA_CREATE);
bValid = false;
}
else
{
rf->mfFileSeek(0, 0, SEEK_SET);
rf->mfFileRead2(0, sizeof(STexCacheFileHeader), &fh);
if (fh.m_SizeOf != sizeof(STexCacheFileHeader) || fh.m_Version != TEXCACHE_VERSION)
bValid = false;
else
{
rf->mfFileRead2(0, sizeof(STexCacheMipHeader), &mh);
if (mh.m_SizeOf != sizeof(STexCacheMipHeader))
bValid = false;
else
if (rf->mfGetHolesSize()/rf->mfGetResourceSize()*100 > 20)
bValid = false;
}
if (bValid)
{
rf->mfClose();
rf->mfOpen(RA_READ|RA_WRITE);
}
else
{
iLog->LogToConsole("Texture cache file '%s' isn't valid (creating new one...)\n", "Textures\\TexturesD3D8.cache");
rf->mfClose();
rf->mfOpen(RA_CREATE);
}
}
gRenDev->m_TexMan->m_TexCache = rf;
return bValid;
}