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/RenderDll/XRenderD3D9/D3DTextureStreamPool.cpp

464 lines
13 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 "RenderDll_precompiled.h"
#include "../Common/Textures/TextureStreamPool.h"
#include "DriverD3D.h"
#if !defined(CHK_RENDTH)
# define CHK_RENDTH assert(gRenDev->m_pRT->IsRenderThread())
#endif
#if !defined(CHK_MAINTH)
# define CHK_MAINTH assert(gRenDev->m_pRT->IsMainThread())
#endif
#if !defined(CHK_MAINORRENDTH)
# define CHK_MAINORRENDTH assert(gRenDev->m_pRT->IsMainThread() || gRenDev->m_pRT->IsRenderThread())
#endif
CryCriticalSection STexPoolItemHdr::s_sSyncLock;
bool STexPoolItem::IsStillUsedByGPU(uint32 nTick)
{
CDeviceTexture* pDeviceTexture = m_pDevTexture;
if (pDeviceTexture)
{
CHK_MAINORRENDTH;
D3DBaseTexture* pD3DTex = pDeviceTexture->GetBaseTexture();
}
return (nTick - m_nFreeTick) < 4;
}
STexPoolItem::STexPoolItem (STexPool* pOwner, CDeviceTexture* pDevTexture, size_t devSize)
: m_pOwner(pOwner)
, m_pTex(NULL)
, m_pDevTexture(pDevTexture)
, m_nDeviceTexSize(devSize)
, m_nFreeTick(0)
, m_nActiveLod(0)
{
assert (pDevTexture);
++pOwner->m_nItems;
}
STexPoolItem::~STexPoolItem()
{
assert (m_pDevTexture);
if (m_pOwner)
{
--m_pOwner->m_nItems;
if (IsFree())
{
--m_pOwner->m_nItemsFree;
}
}
Unlink();
UnlinkFree();
m_pDevTexture->Unbind();
m_pDevTexture->Release();
}
CTextureStreamPoolMgr::CTextureStreamPoolMgr()
{
m_FreeTexPoolItems.m_NextFree = &m_FreeTexPoolItems;
m_FreeTexPoolItems.m_PrevFree = &m_FreeTexPoolItems;
m_nDeviceMemReserved = 0;
m_nDeviceMemInUse = 0;
#ifdef TEXSTRM_USE_FREEPOOL
m_nFreePoolBegin = 0;
m_nFreePoolEnd = 0;
#endif
m_nTick = 0;
#if !defined(_RELEASE)
m_bComputeStats = false;
#endif
}
CTextureStreamPoolMgr::~CTextureStreamPoolMgr()
{
Flush();
}
void CTextureStreamPoolMgr::Flush()
{
AUTO_LOCK(STexPoolItem::s_sSyncLock);
FlushFree();
// Free pools first, as that will attempt to remove streamed textures in release, which will end up
// in the free list.
for (TexturePoolMap::iterator it = m_TexturesPools.begin(), itEnd = m_TexturesPools.end(); it != itEnd; ++it)
{
SAFE_DELETE(it->second);
}
stl::free_container(m_TexturesPools);
// Now nuke the free items.
FlushFree();
#ifdef TEXSTRM_USE_FREEPOOL
while (m_nFreePoolBegin != m_nFreePoolEnd)
{
assert (m_nFreePoolEnd < sizeof(m_FreePool) / sizeof(m_FreePool[0]));
void* p = m_FreePool[m_nFreePoolBegin];
m_nFreePoolBegin = (m_nFreePoolBegin + 1) % MaxFreePool;
::operator delete(p);
}
#endif
}
STexPool* CTextureStreamPoolMgr::GetPool(int nWidth, int nHeight, int nMips, int nArraySize, ETEX_Format eTF, bool bIsSRGB, ETEX_Type eTT)
{
D3DFormat d3dFmt = CTexture::DeviceFormatFromTexFormat(eTF);
if (bIsSRGB)
{
d3dFmt = CTexture::ConvertToSRGBFmt(d3dFmt);
}
TexturePoolKey poolKey((uint16)nWidth, (uint16)nHeight, d3dFmt, (uint8)eTT, (uint8)nMips, (uint16)nArraySize);
TexturePoolMap::iterator it = m_TexturesPools.find(poolKey);
if (it != m_TexturesPools.end())
{
return it->second;
}
return NULL;
}
STexPoolItem* CTextureStreamPoolMgr::GetPoolItem(int nWidth, int nHeight, int nMips, int nArraySize, ETEX_Format eTF, bool bIsSRGB, ETEX_Type eTT, bool bShouldBeCreated, const char* sName, STextureInfo* pTI, bool bCanCreate, bool bWaitForIdle)
{
D3DFormat d3dFmt = CTexture::DeviceFormatFromTexFormat(eTF);
if (bIsSRGB)
{
d3dFmt = CTexture::ConvertToSRGBFmt(d3dFmt);
}
STexPool* pPool = CreatePool(nWidth, nHeight, nMips, nArraySize, d3dFmt, eTT);
if (!pPool)
{
return NULL;
}
AUTO_LOCK(STexPoolItem::s_sSyncLock);
STexPoolItemHdr* pITH = &m_FreeTexPoolItems;
STexPoolItem* pIT = static_cast<STexPoolItem*>(pITH);
bool bFoundACoolingMatch = false;
#ifndef TSP_GC_ALL_ITEMS
if (!pTI)
{
pITH = m_FreeTexPoolItems.m_PrevFree;
pIT = static_cast<STexPoolItem*>(pITH);
// Try to find empty item in the pool
while (pITH != &m_FreeTexPoolItems)
{
if (pIT->m_pOwner == pPool)
{
if (!bWaitForIdle || !pIT->IsStillUsedByGPU(m_nTick))
{
break;
}
bFoundACoolingMatch = true;
}
pITH = pITH->m_PrevFree;
pIT = static_cast<STexPoolItem*>(pITH);
}
}
#endif
if (pITH != &m_FreeTexPoolItems)
{
pIT->UnlinkFree();
#if defined(DO_RENDERLOG)
if IsCVarConstAccess(constexpr) (CRenderer::CV_r_logTexStreaming == 2)
{
gRenDev->LogStrv(SRendItem::m_RecurseLevel[gRenDev->m_RP.m_nProcessThreadID], "Remove from FreePool '%s', [%d x %d], Size: %d\n", sName, pIT->m_pOwner->m_Width, pIT->m_pOwner->m_Height, pIT->m_pOwner->m_Size);
}
#endif
#if !defined(_RELEASE)
++m_frameStats.nSoftCreates;
#endif
--pIT->m_pOwner->m_nItemsFree;
}
else
{
if (!bCanCreate)
{
return NULL;
}
#if defined(TEXSTRM_TEXTURECENTRIC_MEMORY)
if (bFoundACoolingMatch)
{
// Really, really, don't want to create a texture if one will be available soon
if (!bShouldBeCreated)
{
return NULL;
}
}
#endif
// Create API texture for the item in DEFAULT pool
const char* poolTextureName = "StreamingTexturePool";
HRESULT h = S_OK;
CDeviceTexture* pDevTex = NULL;
if (eTT != eTT_Cube)
{
h = gcpRendD3D->m_DevMan.Create2DTexture(poolTextureName, nWidth, nHeight, nMips, nArraySize, STREAMED_TEXTURE_USAGE, Clr_Transparent, d3dFmt, D3DPOOL_DEFAULT, &pDevTex, pTI, bShouldBeCreated);
}
else
{
h = gcpRendD3D->m_DevMan.CreateCubeTexture(poolTextureName, nWidth, nMips, 1, STREAMED_TEXTURE_USAGE, Clr_Transparent, d3dFmt, D3DPOOL_DEFAULT, &pDevTex, pTI, bShouldBeCreated);
}
if (FAILED(h) || !pDevTex)
{
return NULL;
}
#ifdef TEXSTRM_USE_FREEPOOL
if (m_nFreePoolBegin != m_nFreePoolEnd)
{
assert (m_nFreePoolBegin < (sizeof(m_FreePool) / sizeof(m_FreePool[0])));
void* p = m_FreePool[m_nFreePoolBegin];
m_nFreePoolBegin = (m_nFreePoolBegin + 1) % MaxFreePool;
pIT = new (p) STexPoolItem(pPool, pDevTex, pPool->m_Size);
}
else
#endif
{
pIT = new STexPoolItem(pPool, pDevTex, pPool->m_Size);
}
pIT->Link(&pPool->m_ItemsList);
CryInterlockedAddSize(&m_nDeviceMemReserved, (ptrdiff_t)pPool->m_Size);
#if !defined(_RELEASE)
++m_frameStats.nHardCreates;
#endif
}
CryInterlockedAddSize(&m_nDeviceMemInUse, (ptrdiff_t)pIT->m_nDeviceTexSize);
return pIT;
}
void CTextureStreamPoolMgr::ReleaseItem(STexPoolItem* pItem)
{
assert(!pItem->m_NextFree);
#ifdef DO_RENDERLOG
if IsCVarConstAccess(constexpr) (CRenderer::CV_r_logTexStreaming == 2)
{
const char* name = pItem->m_pTex ? pItem->m_pTex->GetSourceName() : "";
gRenDev->LogStrv(SRendItem::m_RecurseLevel[gRenDev->m_RP.m_nProcessThreadID], "Add to FreePool '%s', [%d x %d], Size: %d\n", name, pItem->m_pOwner->m_Width, pItem->m_pOwner->m_Height, pItem->m_pOwner->m_Size);
}
#endif
CryInterlockedAddSize(&m_nDeviceMemInUse, -(ptrdiff_t)pItem->m_nDeviceTexSize);
pItem->m_pTex = NULL;
pItem->m_nFreeTick = m_nTick;
pItem->LinkFree(&m_FreeTexPoolItems);
++pItem->m_pOwner->m_nItemsFree;
#if !defined(_RELEASE)
++m_frameStats.nSoftFrees;
#endif
}
STexPool* CTextureStreamPoolMgr::CreatePool(int nWidth, int nHeight, int nMips, int nArraySize, D3DFormat eTF, ETEX_Type eTT)
{
STexPool* pPool = NULL;
TexturePoolKey poolKey((uint16)nWidth, (uint16)nHeight, eTF, (uint8)eTT, (uint8)nMips, (uint16)nArraySize);
TexturePoolMap::iterator it = m_TexturesPools.find(poolKey);
if (it == m_TexturesPools.end())
{
// Create new pool
pPool = new STexPool;
pPool->m_eTT = eTT;
pPool->m_eFormat = eTF;
pPool->m_Width = nWidth;
pPool->m_Height = nHeight;
pPool->m_nArraySize = nArraySize;
pPool->m_nMips = nMips;
pPool->m_Size = CDeviceTexture::TextureDataSize(nWidth, nHeight, 1, nMips, nArraySize * (eTT == eTT_Cube ? 6 : 1), CTexture::TexFormatFromDeviceFormat(eTF));
pPool->m_nItems = 0;
pPool->m_nItemsFree = 0;
pPool->m_ItemsList.m_Next = &pPool->m_ItemsList;
pPool->m_ItemsList.m_Prev = &pPool->m_ItemsList;
it = m_TexturesPools.insert(std::make_pair(poolKey, pPool)).first;
}
return it->second;
}
void CTextureStreamPoolMgr::FlushFree()
{
STexPoolItemHdr* pITH = m_FreeTexPoolItems.m_PrevFree;
while (pITH != &m_FreeTexPoolItems)
{
STexPoolItemHdr* pITHNext = pITH->m_PrevFree;
STexPoolItem* pIT = static_cast<STexPoolItem*>(pITH);
assert (!pIT->m_pTex);
CryInterlockedAddSize(&m_nDeviceMemReserved, -(ptrdiff_t)pIT->m_nDeviceTexSize);
#ifdef TEXSTRM_USE_FREEPOOL
unsigned int nFreePoolSize = (m_nFreePoolEnd - m_nFreePoolBegin) % MaxFreePool;
if (nFreePoolSize < MaxFreePool - 1) // -1 to avoid needing a separate count
{
pIT->~STexPoolItem();
m_FreePool[m_nFreePoolEnd] = pIT;
m_nFreePoolEnd = (m_nFreePoolEnd + 1) % MaxFreePool;
}
else
#endif
{
delete pIT;
}
pITH = pITHNext;
}
}
void CTextureStreamPoolMgr::GarbageCollect(size_t* nCurTexPoolSize, size_t nLowerPoolLimit, int nMaxItemsToFree)
{
size_t nSize = nCurTexPoolSize ? *nCurTexPoolSize : 1024 * 1024 * 1024;
ptrdiff_t nFreed = 0;
AUTO_LOCK(STexPoolItem::s_sSyncLock);
STexPoolItemHdr* pITH = m_FreeTexPoolItems.m_PrevFree;
while (pITH != &m_FreeTexPoolItems)
{
STexPoolItemHdr* pITHNext = pITH->m_PrevFree;
STexPoolItem* pIT = static_cast<STexPoolItem*>(pITH);
if (!pIT->m_pTex)
{
pITH = pITHNext;
continue;
}
#if defined(TEXSTRM_TEXTURECENTRIC_MEMORY)
if (pIT->m_pOwner->m_nItemsFree > 20 || pIT->m_pOwner->m_Width > 64 || pIT->m_pOwner->m_Height > 64)
#endif
{
if (!pIT->IsStillUsedByGPU(m_nTick))
{
nSize -= pIT->m_nDeviceTexSize;
nFreed += (ptrdiff_t)pIT->m_nDeviceTexSize;
#ifdef TEXSTRM_USE_FREEPOOL
unsigned int nFreePoolSize = (m_nFreePoolEnd - m_nFreePoolBegin) % MaxFreePool;
if (nFreePoolSize < MaxFreePool - 1) // -1 to avoid needing a separate count
{
pIT->~STexPoolItem();
m_FreePool[m_nFreePoolEnd] = pIT;
m_nFreePoolEnd = (m_nFreePoolEnd + 1) % MaxFreePool;
}
else
#endif
{
delete pIT;
}
--nMaxItemsToFree;
#if !defined(_RELEASE)
++m_frameStats.nHardFrees;
#endif
}
}
pITH = pITHNext;
// we release all textures immediately on consoles
#ifndef TSP_GC_ALL_ITEMS
if (nMaxItemsToFree <= 0 || nSize < nLowerPoolLimit)
{
break;
}
#endif
}
CryInterlockedAddSize(&m_nDeviceMemReserved, -nFreed);
CTexture::StreamValidateTexSize();
if (nCurTexPoolSize)
{
*nCurTexPoolSize = nSize;
}
#if !defined(_RELEASE)
if (m_bComputeStats)
{
CryAutoLock<CryCriticalSection> lock(m_statsLock);
m_poolStats.clear();
for (TexturePoolMap::iterator it = m_TexturesPools.begin(), itEnd = m_TexturesPools.end(); it != itEnd; ++it)
{
STexPool* pPool = it->second;
SPoolStats ps;
ps.nWidth = pPool->m_Width;
ps.nHeight = pPool->m_Height;
ps.nMips = pPool->m_nMips;
ps.nFormat = pPool->m_eFormat;
ps.eTT = pPool->m_eTT;
ps.nInUse = pPool->m_nItems - pPool->m_nItemsFree;
ps.nFree = pPool->m_nItemsFree;
ps.nHardCreatesPerFrame = 0;
ps.nSoftCreatesPerFrame = 0;
m_poolStats.push_back(ps);
}
}
#endif
++m_nTick;
}
void CTextureStreamPoolMgr::GetMemoryUsage(ICrySizer* pSizer)
{
SIZER_COMPONENT_NAME(pSizer, "Texture Pools");
pSizer->AddObject(m_TexturesPools);
}