/* * 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. // Description : Common texture manager implementation. #include "StdAfx.h" #include <ImageExtensionHelper.h> #include "Image/CImage.h" #include "Image/DDSImage.h" #include "TextureManager.h" #include <IResourceManager.h> #include "I3DEngine.h" #include "StringUtils.h" // stristr() #include "TextureManager.h" #include "TextureStreamPool.h" #include "TextureHelpers.h" #include <AzCore/IO/SystemFile.h> // for AZ_MAX_PATH_LEN #include "StereoTexture.h" #include <AzCore/Debug/AssetTracking.h> #include <AzCore/std/string/conversions.h> #include <AzFramework/StringFunc/StringFunc.h> #if defined(AZ_RESTRICTED_PLATFORM) #undef AZ_RESTRICTED_SECTION #define TEXTURE_CPP_SECTION_1 1 #define TEXTURE_CPP_SECTION_2 2 #define TEXTURE_CPP_SECTION_3 3 #define TEXTURE_CPP_SECTION_4 4 #define TEXTURE_CPP_SECTION_5 5 #define TEXTURE_CPP_SECTION_6 6 #define TEXTURE_CPP_SECTION_7 7 #endif #if defined(OPENGL_ES) || defined(CRY_USE_METAL) #include "../../XRenderD3D9/DriverD3D.h" // for gcpRendD3D #endif #define TEXTURE_LEVEL_CACHE_PAK "dds0.pak" STexState CTexture::s_sDefState; STexStageInfo CTexture::s_TexStages[MAX_TMU]; int CTexture::s_nStreamingMode; int CTexture::s_nStreamingUpdateMode; bool CTexture::s_bPrecachePhase; bool CTexture::s_bInLevelPhase = false; bool CTexture::s_bPrestreamPhase; int CTexture::s_nStreamingThroughput = 0; float CTexture::s_nStreamingTotalTime = 0; AZStd::vector<STexState, AZ::StdLegacyAllocator> CTexture::s_TexStates; CTextureStreamPoolMgr* CTexture::s_pPoolMgr; AZStd::set<string, AZStd::less<string>, AZ::StdLegacyAllocator> CTexture::s_vTexReloadRequests; CryCriticalSection CTexture::s_xTexReloadLock; #ifdef TEXTURE_GET_SYSTEM_COPY_SUPPORT StaticInstance<CTexture::LowResSystemCopyType> CTexture::s_LowResSystemCopy; #endif StaticInstance<AZStd::mutex> CTexture::m_staticInvalidateCallbacksMutex; bool CTexture::m_bLoadedSystem; CTexture* CTexture::s_ptexMipColors_Diffuse; CTexture* CTexture::s_ptexMipColors_Bump; CTexture* CTexture::s_ptexFromRE[8]; CTexture* CTexture::s_ptexShadowID[8]; CTexture* CTexture::s_ptexShadowMask; CTexture* CTexture::s_ptexCachedShadowMap[MAX_GSM_LODS_NUM]; CTexture* CTexture::s_ptexNearestShadowMap; CTexture* CTexture::s_ptexHeightMapAO[2]; CTexture* CTexture::s_ptexHeightMapAODepth[2]; CTexture* CTexture::s_ptexFromRE_FromContainer[2]; CTexture* CTexture::s_ptexFromObj; CTexture* CTexture::s_ptexSvoTree; CTexture* CTexture::s_ptexSvoTris; CTexture* CTexture::s_ptexSvoGlobalCM; CTexture* CTexture::s_ptexSvoRgbs; CTexture* CTexture::s_ptexSvoNorm; CTexture* CTexture::s_ptexSvoOpac; CTexture* CTexture::s_ptexFromObjCM; CTexture* CTexture::s_ptexRT_2D; CTexture* CTexture::s_ptexSceneNormalsMap; CTexture* CTexture::s_ptexSceneNormalsMapMS; CTexture* CTexture::s_ptexSceneNormalsBent; CTexture* CTexture::s_ptexAOColorBleed; CTexture* CTexture::s_ptexSceneDiffuse; CTexture* CTexture::s_ptexSceneSpecular; #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION TEXTURE_CPP_SECTION_1 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/Texture_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/Texture_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/Texture_cpp_salem.inl" #endif #endif CTexture* CTexture::s_ptexAmbientLookup; // Post-process related textures CTexture* CTexture::s_ptexBackBuffer; CTexture* CTexture::s_ptexModelHudBuffer; CTexture* CTexture::s_ptexPrevBackBuffer[2][2] = { {NULL} }; CTexture* CTexture::s_ptexCached3DHud; CTexture* CTexture::s_ptexCached3DHudScaled; CTexture* CTexture::s_ptexBackBufferScaled[3]; CTexture* CTexture::s_ptexBackBufferScaledTemp[2]; CTexture* CTexture::s_ptexPrevFrameScaled; CTexture* CTexture::s_ptexDepthBufferQuarter; CTexture* CTexture::s_ptexWaterOcean; CTexture* CTexture::s_ptexWaterVolumeTemp; CTexture* CTexture::s_ptexWaterVolumeDDN; CTexture* CTexture::s_ptexWaterVolumeRefl[2]; CTexture* CTexture::s_ptexWaterCaustics[2]; CTexture* CTexture::s_ptexWaterRipplesDDN; CTexture* CTexture::s_ptexRainOcclusion; CTexture* CTexture::s_ptexRainSSOcclusion[2]; CTexture* CTexture::s_ptexRainDropsRT[2]; CTexture* CTexture::s_ptexRT_ShadowPool; CTexture* CTexture::s_ptexRT_ShadowStub; CTexture* CTexture::s_ptexCloudsLM; CTexture* CTexture::s_ptexSceneTarget = NULL; CTexture* CTexture::s_ptexSceneTargetR11G11B10F[2] = {NULL}; CTexture* CTexture::s_ptexSceneTargetScaledR11G11B10F[4] = {NULL}; CTexture* CTexture::s_ptexCurrSceneTarget; CTexture* CTexture::s_ptexCurrentSceneDiffuseAccMap; CTexture* CTexture::s_ptexSceneDiffuseAccMap; CTexture* CTexture::s_ptexSceneSpecularAccMap; CTexture* CTexture::s_ptexSceneDiffuseAccMapMS; CTexture* CTexture::s_ptexSceneSpecularAccMapMS; CTexture* CTexture::s_ptexZTarget; CTexture* CTexture::s_ptexZTargetDownSample[4]; CTexture* CTexture::s_ptexZTargetScaled; CTexture* CTexture::s_ptexZTargetScaled2; CTexture* CTexture::s_ptexHDRTarget; CTexture* CTexture::s_ptexVelocity; CTexture* CTexture::s_ptexVelocityTiles[3] = {NULL}; CTexture* CTexture::s_ptexVelocityObjects[2] = {NULL}; CTexture* CTexture::s_ptexFurZTarget; CTexture* CTexture::s_ptexFurLightAcc; CTexture* CTexture::s_ptexFurPrepass; // Confetti Begin: David Srour CTexture* CTexture::s_ptexGmemStenLinDepth = NULL; // Confetti End CTexture* CTexture::s_ptexHDRTargetPrev = NULL; CTexture* CTexture::s_ptexHDRTargetScaled[4]; CTexture* CTexture::s_ptexHDRTargetScaledTmp[4]; CTexture* CTexture::s_ptexHDRTargetScaledTempRT[4]; CTexture* CTexture::s_ptexHDRDofLayers[2]; CTexture* CTexture::s_ptexSceneCoCHistory[2] = {}; CTexture* CTexture::s_ptexSceneCoC[MIN_DOF_COC_K] = {}; CTexture* CTexture::s_ptexSceneCoCTemp = NULL; CTexture* CTexture::s_ptexHDRTempBloom[2]; CTexture* CTexture::s_ptexHDRFinalBloom; CTexture* CTexture::s_ptexHDRAdaptedLuminanceCur[8]; int CTexture::s_nCurLumTextureIndex; CTexture* CTexture::s_ptexCurLumTexture; CTexture* CTexture::s_ptexHDRToneMaps[NUM_HDR_TONEMAP_TEXTURES]; CTexture* CTexture::s_ptexHDRMeasuredLuminance[MAX_GPU_NUM]; CTexture* CTexture::s_ptexHDRMeasuredLuminanceDummy; CTexture* CTexture::s_ptexSkyDomeMie; CTexture* CTexture::s_ptexSkyDomeRayleigh; CTexture* CTexture::s_ptexSkyDomeMoon; CTexture* CTexture::s_ptexVolObj_Density; CTexture* CTexture::s_ptexVolObj_Shadow; CTexture* CTexture::s_ptexColorChart; CTexture* CTexture::s_ptexSceneTargetScaled; CTexture* CTexture::s_ptexSceneTargetScaledBlurred; CTexture* CTexture::s_ptexStereoL = NULL; CTexture* CTexture::s_ptexStereoR = NULL; CTexture* CTexture::s_ptexFlaresOcclusionRing[MAX_OCCLUSION_READBACK_TEXTURES] = {NULL}; CTexture* CTexture::s_ptexFlaresGather = NULL; SEnvTexture CTexture::s_EnvCMaps[MAX_ENVCUBEMAPS]; SEnvTexture CTexture::s_EnvTexts[MAX_ENVTEXTURES]; StaticInstance<TArray<SEnvTexture>> CTexture::s_CustomRT_2D; StaticInstance<TArray<CTexture>> CTexture::s_ShaderTemplates(EFTT_MAX); bool CTexture::s_ShaderTemplatesInitialized = false; CTexture* CTexture::s_pTexNULL = 0; CTexture* CTexture::s_pBackBuffer; CTexture* CTexture::s_FrontBufferTextures[2] = { NULL }; CTexture* CTexture::s_ptexVolumetricFog = NULL; CTexture* CTexture::s_ptexVolumetricFogDensityColor = NULL; CTexture* CTexture::s_ptexVolumetricFogDensity = NULL; CTexture* CTexture::s_ptexVolumetricClipVolumeStencil = NULL; #if defined(TEXSTRM_DEFERRED_UPLOAD) ID3D11DeviceContext* CTexture::s_pStreamDeferredCtx; #endif #if defined(VOLUMETRIC_FOG_SHADOWS) CTexture* CTexture::s_ptexVolFogShadowBuf[2] = {0}; #endif CTexture* CTexture::s_defaultEnvironmentProbeDummy = nullptr; ETEX_Format CTexture::s_eTFZ = eTF_R32F; //============================================================ SResourceView SResourceView::ShaderResourceView(ETEX_Format nFormat, int nFirstSlice, int nSliceCount, int nMostDetailedMip, int nMipCount, bool bSrgbRead, bool bMultisample) { SResourceView result(0); result.m_Desc.eViewType = eShaderResourceView; result.m_Desc.nFormat = nFormat; result.m_Desc.nFirstSlice = nFirstSlice; result.m_Desc.nSliceCount = nSliceCount; result.m_Desc.nMostDetailedMip = nMostDetailedMip; result.m_Desc.nMipCount = nMipCount; result.m_Desc.bSrgbRead = bSrgbRead ? 1 : 0; result.m_Desc.bMultisample = bMultisample ? 1 : 0; return result; } SResourceView SResourceView::RenderTargetView(ETEX_Format nFormat, int nFirstSlice, int nSliceCount, int nMipLevel, bool bMultisample) { SResourceView result(0); result.m_Desc.eViewType = eRenderTargetView; result.m_Desc.nFormat = nFormat; result.m_Desc.nFirstSlice = nFirstSlice; result.m_Desc.nSliceCount = nSliceCount; result.m_Desc.nMostDetailedMip = nMipLevel; result.m_Desc.bMultisample = bMultisample ? 1 : 0; return result; } SResourceView SResourceView::DepthStencilView(ETEX_Format nFormat, int nFirstSlice, int nSliceCount, int nMipLevel, int nFlags, bool bMultisample) { SResourceView result(0); result.m_Desc.eViewType = eDepthStencilView; result.m_Desc.nFormat = nFormat; result.m_Desc.nFirstSlice = nFirstSlice; result.m_Desc.nSliceCount = nSliceCount; result.m_Desc.nMostDetailedMip = nMipLevel; result.m_Desc.nFlags = nFlags; result.m_Desc.bMultisample = bMultisample ? 1 : 0; return result; } SResourceView SResourceView::UnorderedAccessView(ETEX_Format nFormat, int nFirstSlice, int nSliceCount, int nMipLevel, int nFlags) { SResourceView result(0); result.m_Desc.eViewType = eUnorderedAccessView; result.m_Desc.nFormat = nFormat; result.m_Desc.nFirstSlice = nFirstSlice; result.m_Desc.nSliceCount = nSliceCount; result.m_Desc.nMostDetailedMip = nMipLevel; result.m_Desc.nFlags = (nFlags & FT_USAGE_UAV_RWTEXTURE) ? 1 : 0; return result; } //============================================================ CTexture::~CTexture() { // sizes of these structures should NOT exceed L2 cache line! // offsetof with MSVC's crt and clang produces an error #if defined(PLATFORM_64BIT) && !(defined(AZ_PLATFORM_WINDOWS) && defined(AZ_COMPILER_CLANG)) COMPILE_TIME_ASSERT((offsetof(CTexture, m_composition) - offsetof(CTexture, m_pFileTexMips)) <= 64); COMPILE_TIME_ASSERT((offsetof(CTexture, m_pFileTexMips) % 64) == 0); #endif #ifndef _RELEASE if (!gRenDev->m_pRT->IsRenderThread() || gRenDev->m_pRT->IsRenderLoadingThread()) { __debugbreak(); } #endif #ifndef _RELEASE if (IsStreaming()) { __debugbreak(); } #endif if (gRenDev && gRenDev->m_pRT) { gRenDev->m_pRT->RC_ReleaseDeviceTexture(this); } if (m_pFileTexMips) { Unlink(); StreamState_ReleaseInfo(this, m_pFileTexMips); m_pFileTexMips = NULL; } #ifdef ENABLE_TEXTURE_STREAM_LISTENER if (s_pStreamListener) { s_pStreamListener->OnDestroyedStreamedTexture(this); } #endif #ifndef _RELEASE if (m_bInDistanceSortedList) { __debugbreak(); } #endif #ifdef TEXTURE_GET_SYSTEM_COPY_SUPPORT s_LowResSystemCopy.erase(this); #endif #if defined(USE_UNIQUE_MUTEX_PER_TEXTURE) if (gEnv->IsEditor()) { // Only the editor allocated a unique mutex per texture. delete m_invalidateCallbacksMutex; m_invalidateCallbacksMutex = nullptr; } #endif } void CTexture::RT_ReleaseDevice() { ReleaseDeviceTexture(false); } const CCryNameTSCRC& CTexture::mfGetClassName() { return s_sClassName; } CCryNameTSCRC CTexture::GenName(const char* name, uint32 nFlags) { char buffer[AZ_MAX_PATH_LEN]; IResourceCompilerHelper::GetOutputFilename(name, buffer, sizeof(buffer)); // change texture filename extensions to dds before we compute the crc stack_string strName = buffer; strName.MakeLower(); //'\\' in texture names causing duplication PathUtil::ToUnixPath(strName); if (nFlags & FT_ALPHA) { strName += "_a"; } return CCryNameTSCRC(strName.c_str()); } class StrComp { public: bool operator () (const char* s1, const char* s2) const {return strcmp(s1, s2) < 0; } }; CTexture* CTexture::GetByID(int nID) { CTexture* pTex = NULL; const CCryNameTSCRC& className = mfGetClassName(); CBaseResource* pBR = CBaseResource::GetResource(className, nID, false); if (!pBR) { return CTextureManager::Instance()->GetNoTexture(); } pTex = (CTexture*)pBR; return pTex; } CTexture* CTexture::GetByName(const char* szName, uint32 flags) { CTexture* pTex = NULL; CCryNameTSCRC Name = GenName(szName, flags); CBaseResource* pBR = CBaseResource::GetResource(mfGetClassName(), Name, false); if (!pBR) { return NULL; } pTex = (CTexture*)pBR; return pTex; } CTexture* CTexture::GetByNameCRC(CCryNameTSCRC Name) { CTexture* pTex = NULL; CBaseResource* pBR = CBaseResource::GetResource(mfGetClassName(), Name, false); if (!pBR) { return NULL; } pTex = (CTexture*)pBR; return pTex; } CTexture* CTexture::NewTexture(const char* name, uint32 nFlags, ETEX_Format eTFDst, bool& bFound) { AZ_ASSET_NAMED_SCOPE("CTexture::NewTexture: %s", name); CTexture* pTex = NULL; AZStd::string normalizedFile; AZStd::string fileExtension; AzFramework::StringFunc::Path::GetExtension(name, fileExtension); if (name[0] == '$' || fileExtension.empty()) { //If the name starts with $ or it does not have any extension then it is one of the special texture // that the engine requires and we would not be modifying the name normalizedFile = name; } else { char buffer[AZ_MAX_PATH_LEN]; IResourceCompilerHelper::GetOutputFilename(name, buffer, sizeof(buffer)); // change texture filename extensions to dds normalizedFile = buffer; AZStd::to_lower(normalizedFile.begin(), normalizedFile.end()); PathUtil::ToUnixPath(normalizedFile.c_str()); } CCryNameTSCRC Name = GenName(normalizedFile.c_str(), nFlags); CBaseResource* pBR = CBaseResource::GetResource(mfGetClassName(), Name, false); if (!pBR) { //If a texture name ends in _stereo we want to create a stereo texture const AZStd::string ending = "_stereo"; AZStd::string fullName = normalizedFile.c_str(); size_t nameLength = fullName.length(); size_t endingLength = ending.length(); if (nameLength > endingLength && fullName.compare(nameLength - endingLength, endingLength, ending) == 0) { pTex = new CStereoTexture(normalizedFile.c_str(), eTFDst, nFlags); } else { pTex = new CTexture(nFlags); } pTex->Register(mfGetClassName(), Name); bFound = false; pTex->m_nFlags = nFlags; pTex->m_eTFDst = eTFDst; pTex->m_SrcName = normalizedFile.c_str(); } else { pTex = (CTexture*)pBR; pTex->AddRef(); bFound = true; } return pTex; } void CTexture::SetDevTexture(CDeviceTexture* pDeviceTex) { #if !defined(NULL_RENDERER) SAFE_RELEASE(m_pDevTexture); m_pDevTexture = pDeviceTex; if (m_pDevTexture) { m_pDevTexture->SetNoDelete(!!(m_nFlags & FT_DONT_RELEASE)); } InvalidateDeviceResource(eDeviceResourceDirty); #endif } void CTexture::PostCreate() { m_nUpdateFrameID = gRenDev->GetFrameID(false); m_bPostponed = false; } CTexture* CTexture::CreateTextureObject(const char* name, uint32 nWidth, uint32 nHeight, int nDepth, ETEX_Type eTT, uint32 nFlags, ETEX_Format eTF, int nCustomID) { SYNCHRONOUS_LOADING_TICK(); bool bFound = false; CTexture* pTex = NewTexture(name, nFlags, eTF, bFound); if (bFound) { if (!pTex->m_nWidth) { pTex->m_nWidth = nWidth; } if (!pTex->m_nHeight) { pTex->m_nHeight = nHeight; } pTex->m_nFlags |= nFlags & (FT_DONT_RELEASE | FT_USAGE_RENDERTARGET); return pTex; } pTex->m_nDepth = nDepth; pTex->m_nWidth = nWidth; pTex->m_nHeight = nHeight; pTex->m_eTT = eTT; pTex->m_eTFDst = eTF; pTex->m_nCustomID = nCustomID; pTex->m_SrcName = name; return pTex; } void CTexture::GetMemoryUsage(ICrySizer* pSizer) const { pSizer->Add(*this); pSizer->AddObject(m_SrcName); #ifdef TEXTURE_GET_SYSTEM_COPY_SUPPORT const LowResSystemCopyType::iterator& it = s_LowResSystemCopy.find(this); if (it != CTexture::s_LowResSystemCopy.end()) { pSizer->AddObject((*it).second.m_lowResSystemCopy); } #endif if (m_pFileTexMips) { m_pFileTexMips->GetMemoryUsage(pSizer, m_nMips, m_CacheFileHeader.m_nSides); } } CTexture* CTexture::CreateTextureArray(const char* name, ETEX_Type eType, uint32 nWidth, uint32 nHeight, uint32 nArraySize, int nMips, uint32 nFlags, ETEX_Format eTF, int nCustomID) { assert(eType == eTT_2D || eType == eTT_Cube); if (nArraySize > 255) { assert(0); return NULL; } if (nMips <= 0) { nMips = CTexture::CalcNumMips(nWidth, nHeight); } bool sRGB = (nFlags & FT_USAGE_ALLOWREADSRGB) != 0; nFlags &= ~FT_USAGE_ALLOWREADSRGB; CTexture* pTex = CreateTextureObject(name, nWidth, nHeight, 1, eType, nFlags, eTF, nCustomID); pTex->m_nWidth = nWidth; pTex->m_nHeight = nHeight; pTex->m_nArraySize = nArraySize; pTex->m_nFlags |= eType == eTT_Cube ? FT_REPLICATE_TO_ALL_SIDES : 0; if (nFlags & FT_USAGE_RENDERTARGET) { bool bRes = pTex->CreateRenderTarget(eTF, Clr_Unknown); if (!bRes) { pTex->m_nFlags |= FT_FAILED; } pTex->PostCreate(); } else { STexData td; td.m_eTF = eTF; td.m_nDepth = 1; td.m_nWidth = nWidth; td.m_nHeight = nHeight; td.m_nMips = nMips; td.m_nFlags = sRGB ? FIM_SRGB_READ : 0; bool bRes = pTex->CreateTexture(td); if (!bRes) { pTex->m_nFlags |= FT_FAILED; } pTex->PostCreate(); } pTex->m_nFlags &= ~FT_REPLICATE_TO_ALL_SIDES; return pTex; } CTexture* CTexture::CreateRenderTarget(const char* name, uint32 nWidth, uint32 nHeight, const ColorF& cClear, ETEX_Type eTT, uint32 nFlags, ETEX_Format eTF, int nCustomID) { AZ_ASSET_NAMED_SCOPE("CTexture::CreateRenderTarget: %s", name); CTexture* pTex = CreateTextureObject(name, nWidth, nHeight, 1, eTT, nFlags | FT_USAGE_RENDERTARGET, eTF, nCustomID); pTex->m_nWidth = nWidth; pTex->m_nHeight = nHeight; pTex->m_nFlags |= nFlags; bool bRes = pTex->CreateRenderTarget(eTF, cClear); if (!bRes) { pTex->m_nFlags |= FT_FAILED; } pTex->PostCreate(); return pTex; } // Create2DTextureWithMips is similar to Create2DTexture, but it also propagates the mip argument correctly. // The original Create2DTexture function force sets mips to 1. // This has been separated from Create2DTexture to ensure that we preserve backwards compatibility. bool CTexture::Create2DTextureWithMips(int nWidth, int nHeight, int nMips, int nFlags, const byte* pData, ETEX_Format eTFSrc, ETEX_Format eTFDst) { if (nMips <= 0) { nMips = CTexture::CalcNumMips(nWidth, nHeight); } m_eTFSrc = eTFSrc; m_nMips = nMips; STexData td; td.m_eTF = eTFSrc; td.m_nDepth = 1; td.m_nWidth = nWidth; td.m_nHeight = nHeight; // Propagate mips correctly. (Create2DTexture always sets this to 1) td.m_nMips = nMips; td.m_pData[0] = pData; bool bRes = CreateTexture(td); if (!bRes) { m_nFlags |= FT_FAILED; } PostCreate(); return bRes; } bool CTexture::Create2DTexture(int nWidth, int nHeight, int nMips, int nFlags, const byte* pData, ETEX_Format eTFSrc, ETEX_Format eTFDst) { if (nMips <= 0) { nMips = CTexture::CalcNumMips(nWidth, nHeight); } m_eTFSrc = eTFSrc; m_nMips = nMips; STexData td; td.m_eTF = eTFSrc; td.m_nDepth = 1; td.m_nWidth = nWidth; td.m_nHeight = nHeight; td.m_nMips = 1; td.m_pData[0] = pData; bool bRes = CreateTexture(td); if (!bRes) { m_nFlags |= FT_FAILED; } PostCreate(); return bRes; } CTexture* CTexture::Create2DTexture(const char* szName, int nWidth, int nHeight, int nMips, int nFlags, const byte* pData, ETEX_Format eTFSrc, ETEX_Format eTFDst, bool bAsyncDevTexCreation) { FUNCTION_PROFILER_FAST(GetISystem(), PROFILE_RENDERER, g_bProfilerEnabled); CTexture* pTex = CreateTextureObject(szName, nWidth, nHeight, 1, eTT_2D, nFlags, eTFDst, -1); pTex->m_bAsyncDevTexCreation = bAsyncDevTexCreation; bool bFound = false; pTex->Create2DTexture(nWidth, nHeight, nMips, nFlags, pData, eTFSrc, eTFDst); return pTex; } bool CTexture::Create3DTexture(int nWidth, int nHeight, int nDepth, int nMips, int nFlags, const byte* pData, ETEX_Format eTFSrc, ETEX_Format eTFDst) { m_eTFSrc = eTFSrc; m_nMips = nMips; STexData td; td.m_eTF = eTFSrc; td.m_nWidth = nWidth; td.m_nHeight = nHeight; td.m_nDepth = nDepth; td.m_nMips = nMips; td.m_pData[0] = pData; bool bRes = CreateTexture(td); if (!bRes) { m_nFlags |= FT_FAILED; } PostCreate(); return bRes; } CTexture* CTexture::Create3DTexture(const char* szName, int nWidth, int nHeight, int nDepth, int nMips, int nFlags, const byte* pData, ETEX_Format eTFSrc, ETEX_Format eTFDst) { CTexture* pTex = CreateTextureObject(szName, nWidth, nHeight, nDepth, eTT_3D, nFlags, eTFDst, -1); bool bFound = false; pTex->Create3DTexture(nWidth, nHeight, nDepth, nMips, nFlags, pData, eTFSrc, eTFDst); return pTex; } CTexture* CTexture::Create2DCompositeTexture(const char* szName, int nWidth, int nHeight, int nMips, int nFlags, ETEX_Format eTFDst, const STexComposition* pCompositions, size_t nCompositions) { nFlags |= FT_COMPOSITE; nFlags &= ~FT_DONT_STREAM; bool bFound = false; CTexture* pTex = NewTexture(szName, nFlags, eTFDst, bFound); if (!bFound) { pTex->m_nWidth = nWidth; pTex->m_nHeight = nHeight; pTex->m_nMips = nMips; pTex->m_composition.assign(pCompositions, pCompositions + nCompositions); // Strip all invalid textures from the composition int w = 0; for (int r = 0, c = pTex->m_composition.size(); r != c; ++r) { if (!pTex->m_composition[r].pTexture) { CryWarning(VALIDATOR_MODULE_RENDERER, VALIDATOR_WARNING, "Composition %i for '%s' is missing", r, szName); continue; } if (r != w) { pTex->m_composition[w] = pTex->m_composition[r]; } ++w; } pTex->m_composition.resize(w); if (CTexture::s_bPrecachePhase) { pTex->m_bPostponed = true; pTex->m_bWasUnloaded = true; } else { pTex->StreamPrepareComposition(); } } return pTex; } bool CTexture::Reload() { const byte* pData[6]; int i; bool bOK = false; if (IsStreamed()) { ReleaseDeviceTexture(false); return ToggleStreaming(true); } for (i = 0; i < 6; i++) { pData[i] = 0; } if (m_nFlags & FT_FROMIMAGE) { assert(!(m_nFlags & FT_USAGE_RENDERTARGET)); bOK = LoadFromImage(m_SrcName.c_str()); // true=reloading if (!bOK) { SetNoTexture(m_eTT == eTT_Cube ? CTextureManager::Instance()->GetNoTextureCM() : CTextureManager::Instance()->GetNoTexture() ); } } else if (m_nFlags & (FT_USAGE_RENDERTARGET | FT_USAGE_DYNAMIC)) { bOK = CreateDeviceTexture(pData); assert(bOK); } // Post Create assumes the texture loaded successfully so don't call it if that's not the case if (bOK) { PostCreate(); } return bOK; } CTexture* CTexture::ForName(const char* name, uint32 nFlags, ETEX_Format eTFDst) { SLICE_AND_SLEEP(); AZ_ASSET_NAMED_SCOPE("CTexture::ForName: %s", name); bool bFound = false; CRY_DEFINE_ASSET_SCOPE("Texture", name); CTexture* pTex = NewTexture(name, nFlags, eTFDst, bFound); if (bFound || name[0] == '$') { if (!bFound) { pTex->m_SrcName = name; } else { // switch off streaming for the same texture with the same flags except DONT_STREAM if ((nFlags & FT_DONT_STREAM) != 0 && (pTex->GetFlags() & FT_DONT_STREAM) == 0) { if (!pTex->m_bPostponed) { pTex->ReleaseDeviceTexture(false); } pTex->m_nFlags |= FT_DONT_STREAM; if (!pTex->m_bPostponed) { pTex->Reload(); } } } return pTex; } pTex->m_SrcName = name; #ifndef _RELEASE pTex->m_sAssetScopeName = gEnv->pLog->GetAssetScopeString(); #endif bool bPrecachePhase = CTexture::s_bPrecachePhase && !(nFlags & FT_IGNORE_PRECACHE); ESystemGlobalState currentGlobalState = GetISystem()->GetSystemGlobalState(); const bool levelLoading = currentGlobalState == ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START; // Load textures immediately during level load since texture load // requests during this phase are probably coming from a loading screen. if (levelLoading || !bPrecachePhase) { pTex->Load(eTFDst); } else { // NOTE: attached alpha isn't detectable by flags before the header is loaded, so we do it by file-suffix if (/*(nFlags & FT_TEX_NORMAL_MAP) &&*/ TextureHelpers::VerifyTexSuffix(EFTT_NORMALS, name) && TextureHelpers::VerifyTexSuffix(EFTT_SMOOTHNESS, name)) { nFlags |= FT_HAS_ATTACHED_ALPHA; } pTex->m_eTFDst = eTFDst; pTex->m_nFlags = nFlags; pTex->m_bPostponed = true; pTex->m_bWasUnloaded = true; } return pTex; } struct CompareTextures { bool operator()(const CTexture* a, const CTexture* b) { return (azstricmp(a->GetSourceName(), b->GetSourceName()) < 0); } }; void CTexture::Precache() { LOADING_TIME_PROFILE_SECTION(iSystem); if (!s_bPrecachePhase) { return; } if (!gRenDev) { return; } CryLog("Requesting textures precache ..."); gRenDev->m_pRT->RC_PreloadTextures(); } void CTexture::RT_Precache() { if (gRenDev->CheckDeviceLost()) { return; } LOADING_TIME_PROFILE_SECTION(iSystem); AZ_TRACE_METHOD(); // Disable invalid file access logging if texture streaming is disabled // If texture streaming is turned off, we will hit this on the renderthread // and stall due to the invalid file access stalls ICVar* sysPakLogInvalidAccess = NULL; int pakLogFileAccess = 0; if (!CRenderer::CV_r_texturesstreaming) { if (sysPakLogInvalidAccess = gEnv->pConsole->GetCVar("sys_PakLogInvalidFileAccess")) { pakLogFileAccess = sysPakLogInvalidAccess->GetIVal(); } } CTimeValue t0 = gEnv->pTimer->GetAsyncTime(); CryLog("-- Precaching textures..."); iLog->UpdateLoadingScreen(0); std::vector<CTexture*> TexturesForPrecaching; std::vector<CTexture*> TexturesForComposition; bool bTextureCacheExists = false; { AUTO_LOCK(CBaseResource::s_cResLock); SResourceContainer* pRL = CBaseResource::GetResourcesForClass(CTexture::mfGetClassName()); if (pRL) { TexturesForPrecaching.reserve(pRL->m_RMap.size()); ResourcesMapItor itor; for (itor = pRL->m_RMap.begin(); itor != pRL->m_RMap.end(); itor++) { CTexture* tp = (CTexture*)itor->second; if (!tp) { continue; } if (tp->CTexture::IsPostponed()) { if (tp->CTexture::GetFlags() & FT_COMPOSITE) { TexturesForComposition.push_back(tp); } else { TexturesForPrecaching.push_back(tp); } } } } } // Preload all the post poned textures { if (!gEnv->IsEditor()) { CryLog("=============================== Loading textures ================================"); } std::vector<CTexture*>& Textures = TexturesForPrecaching; std::sort(Textures.begin(), Textures.end(), CompareTextures()); gEnv->pSystem->GetStreamEngine()->PauseStreaming(false, 1 << eStreamTaskTypeTexture); for (uint32 i = 0; i < Textures.size(); i++) { CTexture* tp = Textures[i]; if (!CRenderer::CV_r_texturesstreaming || !tp->m_bStreamPrepared) { tp->m_bPostponed = false; tp->Load(tp->m_eTFDst); } } while (s_StreamPrepTasks.GetNumLive()) { if (gRenDev->m_pRT->IsRenderThread() && !gRenDev->m_pRT->IsRenderLoadingThread()) { StreamState_Update(); StreamState_UpdatePrep(); } else if (gRenDev->m_pRT->IsRenderLoadingThread()) { StreamState_UpdatePrep(); } CrySleep(10); } for (uint32 i = 0; i < Textures.size(); i++) { CTexture* tp = Textures[i]; if (tp->m_bStreamed && tp->m_bForceStreamHighRes) { tp->m_bStreamHighPriority |= 1; tp->m_fpMinMipCur = 0; s_pTextureStreamer->Precache(tp); } } if (!gEnv->IsEditor()) { CryLog("========================== Finished loading textures ============================"); } } { std::vector<CTexture*>& Textures = TexturesForComposition; for (uint32 i = 0; i < Textures.size(); i++) { CTexture* tp = Textures[i]; if (!CRenderer::CV_r_texturesstreaming || !tp->m_bStreamPrepared) { tp->m_bPostponed = false; tp->StreamPrepareComposition(); } } for (uint32 i = 0; i < Textures.size(); i++) { CTexture* tp = Textures[i]; if (tp->m_bStreamed && tp->m_bForceStreamHighRes) { tp->m_bStreamHighPriority |= 1; tp->m_fpMinMipCur = 0; s_pTextureStreamer->Precache(tp); } } } if (bTextureCacheExists) { //GetISystem()->GetIResourceManager()->UnloadLevelCachePak( TEXTURE_LEVEL_CACHE_PAK ); } CTimeValue t1 = gEnv->pTimer->GetAsyncTime(); float dt = (t1 - t0).GetSeconds(); CryLog("Precaching textures done in %.2f seconds", dt); s_bPrecachePhase = false; // Restore pakLogFileAccess if it was disabled during precaching // because texture precaching was disabled if (pakLogFileAccess) { sysPakLogInvalidAccess->Set(pakLogFileAccess); } } bool CTexture::Load(ETEX_Format eTFDst) { LOADING_TIME_PROFILE_SECTION_NAMED_ARGS("CTexture::Load(ETEX_Format eTFDst)", m_SrcName.c_str()); m_bWasUnloaded = false; m_bStreamed = false; #if !defined(NULL_RENDERER) bool bFound = LoadFromImage(m_SrcName.c_str(), eTFDst); // false=not reloading #else bool bFound = false; #endif if (!bFound) { SetNoTexture(m_eTT == eTT_Cube ? CTextureManager::Instance()->GetNoTextureCM() : CTextureManager::Instance()->GetNoTexture()); } m_nFlags |= FT_FROMIMAGE; PostCreate(); return bFound; } bool CTexture::ToggleStreaming(const bool bEnable) { if (!(m_nFlags & (FT_FROMIMAGE | FT_DONT_RELEASE)) || (m_nFlags & FT_DONT_STREAM)) { return false; } AbortStreamingTasks(this); if (bEnable) { if (IsStreamed()) { return true; } ReleaseDeviceTexture(false); m_bStreamed = true; if (StreamPrepare(true)) { return true; } if (m_pFileTexMips) { Unlink(); StreamState_ReleaseInfo(this, m_pFileTexMips); m_pFileTexMips = NULL; } m_bStreamed = false; if (m_bNoTexture) { return true; } } ReleaseDeviceTexture(false); return Reload(); } bool CTexture::LoadFromImage(const char* name, ETEX_Format eTFDst) { LOADING_TIME_PROFILE_SECTION_ARGS(name); if (CRenderer::CV_r_texnoload) { if (SetNoTexture(CTextureManager::Instance()->GetNoTexture() )) { return true; } } string sFileName(name); sFileName.MakeLower(); m_eTFDst = eTFDst; // try to stream-in the texture if (CRenderer::CV_r_texturesstreaming && !(m_nFlags & FT_DONT_STREAM) && (m_eTT == eTT_2D || m_eTT == eTT_Cube)) { m_bStreamed = true; if (StreamPrepare(true)) { assert(m_pDevTexture); return true; } m_nFlags |= FT_DONT_STREAM; m_bStreamed = false; m_bForceStreamHighRes = false; if (m_bNoTexture) { if (m_pFileTexMips) { Unlink(); StreamState_ReleaseInfo(this, m_pFileTexMips); m_pFileTexMips = NULL; m_bStreamed = false; } return true; } } #ifndef _RELEASE CRY_DEFINE_ASSET_SCOPE("Texture", m_sAssetScopeName); #endif if (m_bPostponed) { if (s_pTextureStreamer->BeginPrepare(this, sFileName, (m_nFlags & FT_ALPHA) ? FIM_ALPHA : 0)) { return true; } } uint32 nImageFlags = (m_nFlags & FT_ALPHA) ? FIM_ALPHA : 0; if (I3DEngine* p3DEngine = gEnv->p3DEngine) { if (ITextureLoadHandler* pTextureHandler = p3DEngine->GetTextureLoadHandlerForImage(sFileName.c_str())) { STextureLoadData loadData; loadData.m_pTexture = this; loadData.m_nFlags = m_nFlags; if (pTextureHandler->LoadTextureData(sFileName.c_str(), loadData)) { bool bHasAlphaFlag = false; //we must clear this or else our texture won't load properly if ((m_nFlags & FT_ALPHA) == FT_ALPHA) { m_nFlags &= ~FT_ALPHA; bHasAlphaFlag = true; } _smart_ptr<CImageFile> pImage = CImageFile::mfLoad_mem(sFileName, loadData.m_pData, loadData.m_Width, loadData.m_Height, loadData.m_Format, loadData.m_NumMips, loadData.m_nFlags); m_bisTextureMissing = !pImage || pImage->mfGet_IsImageMissing(); loadData.m_pData = nullptr; bool bLoadResult = Load(&*pImage); if (bHasAlphaFlag) { m_nFlags |= FT_ALPHA; } return bLoadResult; } SetNoTexture(CTextureManager::Instance()->GetNoTexture() ); return true; } } _smart_ptr<CImageFile> pImage = CImageFile::mfLoad_file(sFileName, nImageFlags); m_bisTextureMissing = !pImage || pImage->mfGet_IsImageMissing(); return Load(pImage); } bool CTexture::Load(CImageFile* pImage) { #if !defined(NULL_RENDERER) if (!pImage || pImage->mfGetFormat() == eTF_Unknown) { return false; } LOADING_TIME_PROFILE_SECTION_NAMED_ARGS("CTexture::Load(CImageFile* pImage)", pImage->mfGet_filename().c_str()); // DHX-104: If this failed previously (maybe because the DDS was being generated), we must unset the failure flag // so it doesn't appear to have failed again. m_nFlags &= ~FT_FAILED; if ((m_nFlags & FT_ALPHA) && !pImage->mfIs_image(0)) { SetNoTexture( CTextureManager::Instance()->GetWhiteTexture() ); return true; } const char* name = pImage->mfGet_filename().c_str(); if (pImage->mfGet_Flags() & FIM_SPLITTED) // propagate splitted file flag { m_nFlags |= FT_SPLITTED; } if (pImage->mfGet_Flags() & FIM_X360_NOT_PRETILED) { m_nFlags |= FT_TEX_WAS_NOT_PRE_TILED; } if (pImage->mfGet_Flags() & FIM_NORMALMAP) { if (!(m_nFlags & FT_TEX_NORMAL_MAP) && !CryStringUtils::stristr(name, "_ddn")) { // becomes reported as editor error gEnv->pSystem->Warning(VALIDATOR_MODULE_RENDERER, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE | VALIDATOR_FLAG_TEXTURE, name, "Not a normal map texture attempted to be used as a normal map: %s", name); } } if (!(m_nFlags & FT_ALPHA) && !( pImage->mfGetFormat() == eTF_BC5U || pImage->mfGetFormat() == eTF_BC5S || pImage->mfGetFormat() == eTF_BC7 || pImage->mfGetFormat() == eTF_EAC_RG11 ) && CryStringUtils::stristr(name, "_ddn") != 0 && GetDevTexture()) // improvable code { // becomes reported as editor error gEnv->pSystem->Warning(VALIDATOR_MODULE_RENDERER, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE | VALIDATOR_FLAG_TEXTURE, name, "Wrong format '%s' for normal map texture '%s'", CTexture::GetFormatName(), name); } if (pImage->mfGet_Flags() & FIM_NOTSUPPORTS_MIPS && !(m_nFlags & FT_NOMIPS)) { m_nFlags |= FT_FORCE_MIPS; } if (pImage->mfGet_Flags() & FIM_HAS_ATTACHED_ALPHA) { m_nFlags |= FT_HAS_ATTACHED_ALPHA; // if the image has alpha attached we store this in the CTexture } m_eSrcTileMode = pImage->mfGetTileMode(); STexData td; td.m_nFlags = pImage->mfGet_Flags(); td.m_pData[0] = pImage->mfGet_image(0); td.m_nWidth = pImage->mfGet_width(); td.m_nHeight = pImage->mfGet_height(); td.m_nDepth = pImage->mfGet_depth(); td.m_eTF = pImage->mfGetFormat(); td.m_nMips = pImage->mfGet_numMips(); td.m_fAvgBrightness = pImage->mfGet_avgBrightness(); td.m_cMinColor = pImage->mfGet_minColor(); td.m_cMaxColor = pImage->mfGet_maxColor(); if ((m_nFlags & FT_NOMIPS) || td.m_nMips <= 0) { td.m_nMips = 1; } td.m_pFilePath = pImage->mfGet_filename(); // base range after normalization, fe. [0,1] for 8bit images, or [0,2^15] for RGBE/HDR data if ((td.m_eTF == eTF_R9G9B9E5) || (td.m_eTF == eTF_BC6UH) || (td.m_eTF == eTF_BC6SH)) { td.m_cMinColor /= td.m_cMaxColor.a; td.m_cMaxColor /= td.m_cMaxColor.a; } // check if it's a cubemap if (pImage->mfIs_image(1)) { for (int i = 1; i < 6; i++) { td.m_pData[i] = pImage->mfGet_image(i); } } bool bRes = false; if (pImage) { FormatFixup(td); bRes = CreateTexture(td); } for (int i = 0; i < 6; i++) { if (td.m_pData[i] && td.WasReallocated(i)) { SAFE_DELETE_ARRAY(td.m_pData[i]); } } return bRes; #else SetNoTexture(CTextureManager::Instance()->GetWhiteTexture() ); return true; #endif } bool CTexture::CreateTexture(STexData& td) { m_nWidth = td.m_nWidth; m_nHeight = td.m_nHeight; m_nDepth = td.m_nDepth; m_eTFSrc = td.m_eTF; m_nMips = td.m_nMips; m_fAvgBrightness = td.m_fAvgBrightness; m_cMinColor = td.m_cMinColor; m_cMaxColor = td.m_cMaxColor; m_cClearColor = ColorF(0.0f, 0.0f, 0.0f, 1.0f); m_bUseDecalBorderCol = (td.m_nFlags & FIM_DECAL) != 0; m_bIsSRGB = (td.m_nFlags & FIM_SRGB_READ) != 0; assert(m_nWidth && m_nHeight && m_nMips); if (td.m_pData[1] || (m_nFlags & FT_REPLICATE_TO_ALL_SIDES)) { m_eTT = eTT_Cube; } else { if (m_nDepth > 1 || m_eTT == eTT_3D) { m_eTT = eTT_3D; } else { m_eTT = eTT_2D; } } if (m_eTFDst == eTF_Unknown) { m_eTFDst = m_eTFSrc; } if (!ImagePreprocessing(td)) { return false; } assert(m_nWidth && m_nHeight && m_nMips); const int nMaxTextureSize = gRenDev->GetMaxTextureSize(); if (nMaxTextureSize > 0) { if (m_nWidth > nMaxTextureSize || m_nHeight > nMaxTextureSize) { return false; } } const byte* pData[6]; for (uint32 i = 0; i < 6; i++) { pData[i] = td.m_pData[i]; } bool bRes = CreateDeviceTexture(pData); return bRes; } ETEX_Format CTexture::FormatFixup(ETEX_Format src) { switch (src) { case eTF_L8V8U8X8: return eTF_R8G8B8A8S; case eTF_B8G8R8: return eTF_R8G8B8A8; case eTF_L8V8U8: return eTF_R8G8B8A8S; case eTF_L8: return eTF_R8G8B8A8; case eTF_A8L8: return eTF_R8G8B8A8; case eTF_B5G5R5: return eTF_R8G8B8A8; case eTF_B5G6R5: return eTF_R8G8B8A8; case eTF_B4G4R4A4: return eTF_R8G8B8A8; default: return src; } } bool CTexture::FormatFixup(STexData& td) { const ETEX_Format targetFmt = FormatFixup(td.m_eTF); if (m_eSrcTileMode == eTM_None) { // Try and expand int nSourceSize = CTexture::TextureDataSize(td.m_nWidth, td.m_nHeight, td.m_nDepth, td.m_nMips, 1, td.m_eTF); int nTargetSize = CTexture::TextureDataSize(td.m_nWidth, td.m_nHeight, td.m_nDepth, td.m_nMips, 1, targetFmt); for (int nImage = 0; nImage < sizeof(td.m_pData) / sizeof(td.m_pData[0]); ++nImage) { if (td.m_pData[nImage]) { byte* pNewImage = new byte[nTargetSize]; CTexture::ExpandMipFromFile(pNewImage, nTargetSize, td.m_pData[nImage], nSourceSize, td.m_eTF); td.AssignData(nImage, pNewImage); } } td.m_eTF = targetFmt; } else { #ifndef _RELEASE if (targetFmt != td.m_eTF) { __debugbreak(); } #endif } return true; } bool CTexture::ImagePreprocessing(STexData& td) { FUNCTION_PROFILER_FAST(GetISystem(), PROFILE_RENDERER, g_bProfilerEnabled); const char* pTexFileName = td.m_pFilePath ? td.m_pFilePath : "$Unknown"; const ETEX_Format eTFDst = ClosestFormatSupported(m_eTFDst); if (eTFDst == eTF_Unknown) { td.m_pData[0] = td.m_pData[1] = td.m_pData[2] = td.m_pData[3] = td.m_pData[4] = td.m_pData[5] = 0; m_nWidth = m_nHeight = m_nDepth = m_nMips = 0; #if !defined(_RELEASE) TextureError(pTexFileName, "Trying to create a texture with unsupported target format %s!", NameForTextureFormat(eTFDst)); #endif return false; } const ETEX_Format eTF = td.m_eTF; const bool fmtConversionNeeded = eTFDst != m_eTFDst || eTF != eTFDst; #if !(defined(WIN32) || defined(WIN64)) || defined(OPENGL) || defined(NULL_RENDERER) if (fmtConversionNeeded) { td.m_pData[0] = td.m_pData[1] = td.m_pData[2] = td.m_pData[3] = td.m_pData[4] = td.m_pData[5] = 0; m_nWidth = m_nHeight = m_nDepth = m_nMips = 0; # if !defined(_RELEASE) TextureError(pTexFileName, "Trying an image format conversion from %s to %s. This is not supported on this platform!", NameForTextureFormat(eTF), NameForTextureFormat(eTFDst)); # endif return false; } #else const bool doProcessing = fmtConversionNeeded && (m_nFlags & FT_TEX_FONT) == 0; // we generate the font in native format if (doProcessing) { m_eTFSrc = eTFDst; m_eTFDst = eTFDst; const int nSrcWidth = td.m_nWidth; const int nSrcHeight = td.m_nHeight; for (int i = 0; i < 6; i++) { const byte* pTexData = td.m_pData[i]; if (pTexData) { int nOutSize = 0; const byte* pNewData = Convert(pTexData, nSrcWidth, nSrcHeight, td.m_nMips, eTF, eTFDst, nOutSize, true); if (pNewData) { td.AssignData(i, pNewData); } } } } #endif #if defined(TEXTURE_GET_SYSTEM_COPY_SUPPORT) && !defined(NULL_RENDERER) if (m_nFlags & FT_KEEP_LOWRES_SYSCOPY) { PrepareLowResSystemCopy(td.m_pData[0], true); } #endif return true; } int CTexture::CalcNumMips(int nWidth, int nHeight) { int nMips = 0; while (nWidth || nHeight) { if (!nWidth) { nWidth = 1; } if (!nHeight) { nHeight = 1; } nWidth >>= 1; nHeight >>= 1; nMips++; } //For DX11 hardware, the number of mips must be between 1 and 7, inclusive //0 is a valid result but means that D3D11 will generate a full series of mipmaps if (nMips > 7) { return 7; } return nMips; } uint32 CTexture::TextureDataSize(uint32 nWidth, uint32 nHeight, uint32 nDepth, uint32 nMips, uint32 nSlices, const ETEX_Format eTF, ETEX_TileMode eTM) { if (eTF == eTF_Unknown) { return 0; } if (eTM != eTM_None) { #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION TEXTURE_CPP_SECTION_2 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/Texture_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/Texture_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/Texture_cpp_salem.inl" #endif #endif #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION TEXTURE_CPP_SECTION_3 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/Texture_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/Texture_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/Texture_cpp_salem.inl" #endif #endif __debugbreak(); return 0; } else { const Vec2i BlockDim = GetBlockDim(eTF); const int nBytesPerBlock = CImageExtensionHelper::BytesPerBlock(eTF); uint32 nSize = 0; while ((nWidth || nHeight || nDepth) && nMips) { nWidth = max(1U, nWidth); nHeight = max(1U, nHeight); nDepth = max(1U, nDepth); nSize += ((nWidth + BlockDim.x - 1) / BlockDim.x) * ((nHeight + BlockDim.y - 1) / BlockDim.y) * nDepth * nBytesPerBlock; nWidth >>= 1; nHeight >>= 1; nDepth >>= 1; --nMips; } return nSize * nSlices; } } bool CTexture::IsInPlaceFormat(const ETEX_Format fmt) { switch (fmt) { case eTF_R8G8B8A8S: case eTF_R8G8B8A8: case eTF_A8: case eTF_R8: case eTF_R8S: case eTF_R16: case eTF_R16U: case eTF_R16G16U: case eTF_R10G10B10A2UI: case eTF_R16F: case eTF_R32F: case eTF_R8G8: case eTF_R8G8S: case eTF_R16G16: case eTF_R16G16S: case eTF_R16G16F: case eTF_R11G11B10F: case eTF_R10G10B10A2: case eTF_R16G16B16A16: case eTF_R16G16B16A16S: case eTF_R16G16B16A16F: case eTF_R32G32B32A32F: case eTF_CTX1: case eTF_BC1: case eTF_BC2: case eTF_BC3: case eTF_BC4U: case eTF_BC4S: case eTF_BC5U: case eTF_BC5S: #if defined(CRY_DDS_DX10_SUPPORT) case eTF_BC6UH: case eTF_BC6SH: case eTF_BC7: case eTF_R9G9B9E5: #endif case eTF_EAC_R11: case eTF_EAC_RG11: case eTF_ETC2: case eTF_ETC2A: case eTF_B8G8R8A8: case eTF_B8G8R8X8: #ifdef CRY_USE_METAL case eTF_PVRTC2: case eTF_PVRTC4: #endif #if defined(ANDROID) || defined(CRY_USE_METAL) case eTF_ASTC_4x4: case eTF_ASTC_5x4: case eTF_ASTC_5x5: case eTF_ASTC_6x5: case eTF_ASTC_6x6: case eTF_ASTC_8x5: case eTF_ASTC_8x6: case eTF_ASTC_8x8: case eTF_ASTC_10x5: case eTF_ASTC_10x6: case eTF_ASTC_10x8: case eTF_ASTC_10x10: case eTF_ASTC_12x10: case eTF_ASTC_12x12: #endif return true; default: return false; } } void CTexture::ExpandMipFromFile(byte* dest, const int destSize, const byte* src, const int srcSize, const ETEX_Format fmt) { if (IsInPlaceFormat(fmt)) { assert(destSize == srcSize); if (dest != src) { cryMemcpy(dest, src, srcSize); } return; } // upload mip from file with conversions depending on format and platform specifics switch (fmt) { case eTF_B8G8R8: for (int i = srcSize / 3 - 1; i >= 0; --i) { dest[i * 4 + 0] = src[i * 3 + 2]; dest[i * 4 + 1] = src[i * 3 + 1]; dest[i * 4 + 2] = src[i * 3 + 0]; dest[i * 4 + 3] = 255; } break; case eTF_L8V8U8X8: assert(destSize == srcSize); if (dest != src) { cryMemcpy(dest, src, srcSize); } for (int i = srcSize / 4 - 1; i >= 0; --i) { dest[i * 4 + 0] = src[i * 3 + 0]; dest[i * 4 + 1] = src[i * 3 + 1]; dest[i * 4 + 2] = src[i * 3 + 2]; dest[i * 4 + 3] = src[i * 3 + 3]; } break; case eTF_L8V8U8: for (int i = srcSize / 3 - 1; i >= 0; --i) { dest[i * 4 + 0] = src[i * 3 + 0]; dest[i * 4 + 1] = src[i * 3 + 1]; dest[i * 4 + 2] = src[i * 3 + 2]; dest[i * 4 + 3] = 255; } break; case eTF_L8: for (int i = srcSize - 1; i >= 0; --i) { const byte bSrc = src[i]; dest[i * 4 + 0] = bSrc; dest[i * 4 + 1] = bSrc; dest[i * 4 + 2] = bSrc; dest[i * 4 + 3] = 255; } break; case eTF_A8L8: for (int i = srcSize - 1; i >= 0; i -= 2) { const byte bSrcL = src[i - 1]; const byte bSrcA = src[i - 0]; dest[i * 4 + 0] = bSrcL; dest[i * 4 + 1] = bSrcL; dest[i * 4 + 2] = bSrcL; dest[i * 4 + 3] = bSrcA; } break; case eTF_B5G5R5: case eTF_B5G6R5: case eTF_B4G4R4A4: default: assert(0); } } bool CTexture::Invalidate(int nNewWidth, int nNewHeight, ETEX_Format eTF) { bool bRelease = false; if (nNewWidth > 0 && nNewWidth != m_nWidth) { m_nWidth = nNewWidth; bRelease = true; } if (nNewHeight > 0 && nNewHeight != m_nHeight) { m_nHeight = nNewHeight; bRelease = true; } if (eTF != eTF_Unknown && eTF != m_eTFDst) { m_eTFDst = eTF; bRelease = true; } if (!m_pDevTexture) { return false; } if (bRelease) { if (m_nFlags & FT_FORCE_MIPS) { m_nMips = 1; } ReleaseDeviceTexture(true); } return bRelease; } SResourceView& CTexture::GetResourceView(const SResourceView& rvDesc) { assert(m_pRenderTargetData); int nIndex = m_pRenderTargetData->m_ResourceViews.Find(rvDesc); if (nIndex < 0) { SResourceView* pRvDesc = m_pRenderTargetData->m_ResourceViews.AddIndex(1); pRvDesc->m_Desc = rvDesc.m_Desc; pRvDesc->m_pDeviceResourceView = CreateDeviceResourceView(rvDesc); nIndex = m_pRenderTargetData->m_ResourceViews.size() - 1; } return m_pRenderTargetData->m_ResourceViews[nIndex]; } D3DShaderResourceView* CTexture::GetShaderResourceView(SResourceView::KeyType resourceViewID /*= SResourceView::DefaultView*/, bool bLegacySrgbLookup /*= false*/) { if ((int64)resourceViewID <= (int64)SResourceView::DefaultView) { void* pResult = m_pDeviceShaderResource; if (resourceViewID == SResourceView::DefaultViewMS && m_pRenderTargetData && m_pRenderTargetData->m_pDeviceTextureMSAA) { SResourceView& pMultisampledRV = GetResourceView(SResourceView::ShaderResourceView(m_eTFDst, 0, -1, 0, -1, false, true)); pResult = pMultisampledRV.m_pDeviceResourceView; } // NOTE: "m_pDeviceShaderResourceSRGB != nullptr" implies FT_USAGE_ALLOWREADSRGB if ((resourceViewID == SResourceView::DefaultViewSRGB || bLegacySrgbLookup) && m_pDeviceShaderResourceSRGB) { pResult = m_pDeviceShaderResourceSRGB; } return (D3DShaderResourceView*)pResult; } else { return (D3DShaderResourceView*)GetResourceView(resourceViewID).m_pDeviceResourceView; } } void CTexture::SetShaderResourceView(D3DShaderResourceView* pDeviceShaderResource, bool bMultisampled) { if (bMultisampled && m_pRenderTargetData && m_pRenderTargetData->m_pDeviceTextureMSAA) { SResourceView& pMultisampledRV = GetResourceView(SResourceView::ShaderResourceView(m_eTFDst, 0, -1, 0, -1, false, true)); if (pMultisampledRV.m_pDeviceResourceView != pDeviceShaderResource) { pMultisampledRV.m_pDeviceResourceView = pDeviceShaderResource; InvalidateDeviceResource(eDeviceResourceViewDirty); } } else { if (m_pDeviceShaderResource != pDeviceShaderResource) { m_pDeviceShaderResource = pDeviceShaderResource; InvalidateDeviceResource(eDeviceResourceViewDirty); } } } D3DUnorderedAccessView* CTexture::GetDeviceUAV() { const SResourceView& rvDesc = m_pRenderTargetData ? GetResourceView(SResourceView::UnorderedAccessView(m_eTFDst, 0, -1, 0, m_nFlags)) : NULL; return (D3DUnorderedAccessView*)rvDesc.m_pDeviceResourceView; } D3DDepthSurface* CTexture::GetDeviceDepthStencilSurf(int nFirstSlice, int nSliceCount) { const SResourceView& rvDesc = GetResourceView(SResourceView::DepthStencilView(m_eTFDst, nFirstSlice, nSliceCount)); return (D3DDepthSurface*)rvDesc.m_pDeviceResourceView; } byte* CTexture::GetData32(int nSide, int nLevel, byte* pDst, ETEX_Format eDstFormat) { #if (defined(WIN32) || defined(WIN64)) && !defined(NULL_RENDERER) CDeviceTexture* pDevTexture = GetDevTexture(); pDevTexture->DownloadToStagingResource(D3D11CalcSubresource(nLevel, nSide, m_nMips), [&](void* pData, uint32 rowPitch, uint32 slicePitch) { if (m_eTFDst != eTF_R8G8B8A8) { int nOutSize = 0; if (m_eTFSrc == eDstFormat && pDst) { memcpy(pDst, pData, GetDeviceDataSize()); } else { pDst = Convert((byte*)pData, m_nWidth, m_nHeight, 1, m_eTFSrc, eDstFormat, nOutSize, true); } } else { if (!pDst) { pDst = new byte[m_nWidth * m_nHeight * 4]; } memcpy(pDst, pData, m_nWidth * m_nHeight * 4); } return true; }); return pDst; #else return 0; #endif } const int CTexture::GetSize(bool bIncludePool) const { int nSize = sizeof(CTexture); nSize += m_SrcName.capacity(); if (m_pRenderTargetData) { nSize += sizeof(*m_pRenderTargetData); } if (m_pFileTexMips) { nSize += m_pFileTexMips->GetSize(m_nMips, m_CacheFileHeader.m_nSides); if (bIncludePool && m_pFileTexMips->m_pPoolItem) { nSize += m_pFileTexMips->m_pPoolItem->GetSize(); } } return nSize; } void CTexture::Init() { SDynTexture::Init(); InitStreaming(); CTexture::s_TexStates.reserve(300); // this likes to expand, so it'd be nice if it didn't; 300 => ~6Kb, there were 171 after one level SDynTexture2::Init(eTP_Clouds); } void CTexture::PostInit() { LOADING_TIME_PROFILE_SECTION; if (!gRenDev->IsShaderCacheGenMode()) { CTexture::LoadDefaultSystemTextures(); } } int __cdecl TexCallback(const VOID * arg1, const VOID * arg2) { CTexture** pi1 = (CTexture**)arg1; CTexture** pi2 = (CTexture**)arg2; CTexture* ti1 = *pi1; CTexture* ti2 = *pi2; if (ti1->GetDeviceDataSize() > ti2->GetDeviceDataSize()) { return -1; } if (ti1->GetDeviceDataSize() < ti2->GetDeviceDataSize()) { return 1; } return azstricmp(ti1->GetSourceName(), ti2->GetSourceName()); } int __cdecl TexCallbackMips(const VOID * arg1, const VOID * arg2) { CTexture** pi1 = (CTexture**)arg1; CTexture** pi2 = (CTexture**)arg2; CTexture* ti1 = *pi1; CTexture* ti2 = *pi2; int nSize1, nSize2; nSize1 = ti1->GetActualSize(); nSize2 = ti2->GetActualSize(); if (nSize1 > nSize2) { return -1; } if (nSize1 < nSize2) { return 1; } return azstricmp(ti1->GetSourceName(), ti2->GetSourceName()); } void CTexture::Update() { FUNCTION_PROFILER_RENDERER; CRenderer* rd = gRenDev; char buf[256] = ""; // reload pending texture reload requests { AZStd::set<string, AZStd::less<string>, AZ::StdLegacyAllocator> queue; s_xTexReloadLock.Lock(); s_vTexReloadRequests.swap(queue); s_xTexReloadLock.Unlock(); for (auto i = queue.begin(); i != queue.end(); i++) { ReloadFile(*i); } } CTexture::s_bStreamingFromHDD = gEnv->pSystem->GetStreamEngine()->IsStreamDataOnHDD(); CTexture::s_nStatsStreamPoolInUseMem = CTexture::s_pPoolMgr->GetInUseSize(); s_pTextureStreamer->ApplySchedule(ITextureStreamer::eASF_Full); s_pTextureStreamer->BeginUpdateSchedule(); #ifdef ENABLE_TEXTURE_STREAM_LISTENER StreamUpdateStats(); #endif SDynTexture::Tick(); SResourceContainer* pRL = CBaseResource::GetResourcesForClass(CTexture::mfGetClassName()); ResourcesMapItor itor; if ((s_nStreamingMode != CRenderer::CV_r_texturesstreaming) || (s_nStreamingUpdateMode != CRenderer::CV_r_texturesstreamingUpdateType)) { InitStreaming(); } if (pRL) { #ifndef CONSOLE_CONST_CVAR_MODE uint32 i; if (CRenderer::CV_r_texlog == 2 || CRenderer::CV_r_texlog == 3 || CRenderer::CV_r_texlog == 4) { AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; TArray<CTexture*> Texs; int Size = 0; int PartSize = 0; if (CRenderer::CV_r_texlog == 2 || CRenderer::CV_r_texlog == 3) { for (itor = pRL->m_RMap.begin(); itor != pRL->m_RMap.end(); itor++) { CTexture* tp = (CTexture*)itor->second; if (CRenderer::CV_r_texlog == 3 && tp->IsNoTexture()) { Texs.AddElem(tp); } else if (CRenderer::CV_r_texlog == 2 && !tp->IsNoTexture() && tp->m_pFileTexMips) // (tp->GetFlags() & FT_FROMIMAGE)) { Texs.AddElem(tp); } } if (CRenderer::CV_r_texlog == 3) { CryLogAlways("Logging to MissingTextures.txt..."); fileHandle = fxopen("MissingTextures.txt", "w"); } else { CryLogAlways("Logging to UsedTextures.txt..."); fileHandle = fxopen("UsedTextures.txt", "w"); } AZ::IO::Print(fileHandle, "*** All textures: ***\n"); if (Texs.Num()) { qsort(&Texs[0], Texs.Num(), sizeof(CTexture*), TexCallbackMips); } for (i = 0; i < Texs.Num(); i++) { int w = Texs[i]->GetWidth(); int h = Texs[i]->GetHeight(); int nTSize = Texs[i]->m_pFileTexMips->GetSize(Texs[i]->GetNumMips(), Texs[i]->GetNumSides()); AZ::IO::Print(fileHandle, "%d\t\t%d x %d\t\tType: %s\t\tMips: %d\t\tFormat: %s\t\t(%s)\n", nTSize, w, h, Texs[i]->NameForTextureType(Texs[i]->GetTextureType()), Texs[i]->GetNumMips(), Texs[i]->NameForTextureFormat(Texs[i]->GetDstFormat()), Texs[i]->GetName()); //Size += Texs[i]->GetDataSize(); Size += nTSize; PartSize += Texs[i]->GetDeviceDataSize(); } AZ::IO::Print(fileHandle, "*** Total Size: %d\n\n", Size /*, PartSize, PartSize */); Texs.Free(); } for (itor = pRL->m_RMap.begin(); itor != pRL->m_RMap.end(); itor++) { CTexture* tp = (CTexture*)itor->second; if (tp->m_nAccessFrameID == rd->m_RP.m_TI[rd->m_RP.m_nProcessThreadID].m_nFrameUpdateID) { Texs.AddElem(tp); } } if (fileHandle != AZ::IO::InvalidHandle) { gEnv->pFileIO->Close(fileHandle); fileHandle = AZ::IO::InvalidHandle; } fileHandle = fxopen("UsedTextures_Frame.txt", "w"); if (fileHandle != AZ::IO::InvalidHandle) { AZ::IO::Print(fileHandle, "\n\n*** Textures used in current frame: ***\n"); } else { rd->TextToScreenColor(4, 13, 1, 1, 0, 1, "*** Textures used in current frame: ***"); } int nY = 17; if (Texs.Num()) { qsort(&Texs[0], Texs.Num(), sizeof(CTexture*), TexCallback); } Size = 0; for (i = 0; i < Texs.Num(); i++) { if (fileHandle != AZ::IO::InvalidHandle) { AZ::IO::Print(fileHandle, "%.3fKb\t\tType: %s\t\tFormat: %s\t\t(%s)\n", Texs[i]->GetDeviceDataSize() / 1024.0f, CTexture::NameForTextureType(Texs[i]->GetTextureType()), CTexture::NameForTextureFormat(Texs[i]->GetDstFormat()), Texs[i]->GetName()); } else { sprintf_s(buf, "%.3fKb Type: %s Format: %s (%s)", Texs[i]->GetDeviceDataSize() / 1024.0f, CTexture::NameForTextureType(Texs[i]->GetTextureType()), CTexture::NameForTextureFormat(Texs[i]->GetDstFormat()), Texs[i]->GetName()); rd->TextToScreenColor(4, nY, 0, 1, 0, 1, buf); nY += 3; } PartSize += Texs[i]->GetDeviceDataSize(); Size += Texs[i]->GetDataSize(); } if (fileHandle != AZ::IO::InvalidHandle) { AZ::IO::Print(fileHandle, "*** Total Size: %.3fMb, Device Size: %.3fMb\n\n", Size / (1024.0f * 1024.0f), PartSize / (1024.0f * 1024.0f)); } else { sprintf_s(buf, "*** Total Size: %.3fMb, Device Size: %.3fMb", Size / (1024.0f * 1024.0f), PartSize / (1024.0f * 1024.0f)); rd->TextToScreenColor(4, nY + 1, 0, 1, 1, 1, buf); } Texs.Free(); for (itor = pRL->m_RMap.begin(); itor != pRL->m_RMap.end(); itor++) { CTexture* tp = (CTexture*)itor->second; if (tp && !tp->IsNoTexture()) { Texs.AddElem(tp); } } if (fileHandle != AZ::IO::InvalidHandle) { gEnv->pFileIO->Close(fileHandle); fileHandle = AZ::IO::InvalidHandle; } fileHandle = fxopen("UsedTextures_All.txt", "w"); if (fileHandle != AZ::IO::InvalidHandle) { AZ::IO::Print(fileHandle, "\n\n*** All Existing Textures: ***\n"); } else { rd->TextToScreenColor(4, 13, 1, 1, 0, 1, "*** Textures loaded: ***"); } if (Texs.Num()) { qsort(&Texs[0], Texs.Num(), sizeof(CTexture*), TexCallback); } Size = 0; for (i = 0; i < Texs.Num(); i++) { if (fileHandle != AZ::IO::InvalidHandle) { int w = Texs[i]->GetWidth(); int h = Texs[i]->GetHeight(); AZ::IO::Print(fileHandle, "%d\t\t%d x %d\t\t%d mips (%.3fKb)\t\tType: %s \t\tFormat: %s\t\t(%s)\n", Texs[i]->GetDataSize(), w, h, Texs[i]->GetNumMips(), Texs[i]->GetDeviceDataSize() / 1024.0f, CTexture::NameForTextureType(Texs[i]->GetTextureType()), CTexture::NameForTextureFormat(Texs[i]->GetDstFormat()), Texs[i]->GetName()); } else { sprintf_s(buf, "%.3fKb Type: %s Format: %s (%s)", Texs[i]->GetDataSize() / 1024.0f, CTexture::NameForTextureType(Texs[i]->GetTextureType()), CTexture::NameForTextureFormat(Texs[i]->GetDstFormat()), Texs[i]->GetName()); rd->TextToScreenColor(4, nY, 0, 1, 0, 1, buf); nY += 3; } Size += Texs[i]->GetDeviceDataSize(); } if (fileHandle != AZ::IO::InvalidHandle) { AZ::IO::Print(fileHandle, "*** Total Size: %.3fMb\n\n", Size / (1024.0f * 1024.0f)); } else { sprintf_s(buf, "*** Total Size: %.3fMb", Size / (1024.0f * 1024.0f)); rd->TextToScreenColor(4, nY + 1, 0, 1, 1, 1, buf); } Texs.Free(); for (itor = pRL->m_RMap.begin(); itor != pRL->m_RMap.end(); itor++) { CTexture* tp = (CTexture*)itor->second; if (tp && !tp->IsNoTexture() && !tp->IsStreamed()) { Texs.AddElem(tp); } } if (fileHandle != AZ::IO::InvalidHandle) { gEnv->pFileIO->Close(fileHandle); fileHandle = AZ::IO::InvalidHandle; } if (CRenderer::CV_r_texlog != 4) { CRenderer::CV_r_texlog = 0; } } else if (CRenderer::CV_r_texlog == 1) { //char *str = GetTexturesStatusText(); TArray<CTexture*> Texs; //TArray<CTexture *> TexsNM; for (itor = pRL->m_RMap.begin(); itor != pRL->m_RMap.end(); itor++) { CTexture* tp = (CTexture*)itor->second; if (tp && !tp->IsNoTexture()) { Texs.AddElem(tp); //if (tp->GetFlags() & FT_TEX_NORMAL_MAP) // TexsNM.AddElem(tp); } } if (Texs.Num()) { qsort(&Texs[0], Texs.Num(), sizeof(CTexture*), TexCallback); } int64 AllSize = 0; int64 Size = 0; int64 PartSize = 0; int64 NonStrSize = 0; int nNoStr = 0; int64 SizeNM = 0; int64 SizeDynCom = 0; int64 SizeDynAtl = 0; int64 PartSizeNM = 0; int nNumTex = 0; int nNumTexNM = 0; int nNumTexDynAtl = 0; int nNumTexDynCom = 0; for (i = 0; i < Texs.Num(); i++) { CTexture* tex = Texs[i]; const uint32 texFlags = tex->GetFlags(); const int texDataSize = tex->GetDataSize(); const int texDeviceDataSize = tex->GetDeviceDataSize(); if (tex->GetDevTexture() && !(texFlags & (FT_USAGE_DYNAMIC | FT_USAGE_RENDERTARGET))) { AllSize += texDataSize; if (!Texs[i]->IsStreamed()) { NonStrSize += texDataSize; nNoStr++; } } if (texFlags & (FT_USAGE_RENDERTARGET | FT_USAGE_DYNAMIC)) { if (texFlags & FT_USAGE_ATLAS) { ++nNumTexDynAtl; SizeDynAtl += texDataSize; } else { ++nNumTexDynCom; SizeDynCom += texDataSize; } } else if (0 == (texFlags & FT_TEX_NORMAL_MAP)) { if (!Texs[i]->IsUnloaded()) { ++nNumTex; Size += texDataSize; PartSize += texDeviceDataSize; } } else { if (!Texs[i]->IsUnloaded()) { ++nNumTexNM; SizeNM += texDataSize; PartSizeNM += texDeviceDataSize; } } } sprintf_s(buf, "All texture objects: %d (Size: %.3fMb), NonStreamed: %d (Size: %.3fMb)", Texs.Num(), AllSize / (1024.0 * 1024.0), nNoStr, NonStrSize / (1024.0 * 1024.0)); rd->TextToScreenColor(4, 13, 1, 1, 0, 1, buf); sprintf_s(buf, "All Loaded Texture Maps: %d (All MIPS: %.3fMb, Loaded MIPS: %.3fMb)", nNumTex, Size / (1024.0f * 1024.0f), PartSize / (1024.0 * 1024.0)); rd->TextToScreenColor(4, 16, 1, 1, 0, 1, buf); sprintf_s(buf, "All Loaded Normal Maps: %d (All MIPS: %.3fMb, Loaded MIPS: %.3fMb)", nNumTexNM, SizeNM / (1024.0 * 1024.0), PartSizeNM / (1024.0 * 1024.0)); rd->TextToScreenColor(4, 19, 1, 1, 0, 1, buf); sprintf_s(buf, "All Dynamic textures: %d (%.3fMb), %d Atlases (%.3fMb), %d Separared (%.3fMb)", nNumTexDynAtl + nNumTexDynCom, (SizeDynAtl + SizeDynCom) / (1024.0 * 1024.0), nNumTexDynAtl, SizeDynAtl / (1024.0 * 1024.0), nNumTexDynCom, SizeDynCom / (1024.0 * 1024.0)); rd->TextToScreenColor(4, 22, 1, 1, 0, 1, buf); Texs.Free(); for (itor = pRL->m_RMap.begin(); itor != pRL->m_RMap.end(); itor++) { CTexture* tp = (CTexture*)itor->second; if (tp && !tp->IsNoTexture() && tp->m_nAccessFrameID == rd->m_RP.m_TI[rd->m_RP.m_nProcessThreadID].m_nFrameUpdateID) { Texs.AddElem(tp); } } if (Texs.Num()) { qsort(&Texs[0], Texs.Num(), sizeof(CTexture*), TexCallback); } Size = 0; SizeDynAtl = 0; SizeDynCom = 0; PartSize = 0; NonStrSize = 0; for (i = 0; i < Texs.Num(); i++) { Size += Texs[i]->GetDataSize(); if (Texs[i]->GetFlags() & (FT_USAGE_DYNAMIC | FT_USAGE_RENDERTARGET)) { if (Texs[i]->GetFlags() & FT_USAGE_ATLAS) { SizeDynAtl += Texs[i]->GetDataSize(); } else { SizeDynCom += Texs[i]->GetDataSize(); } } else { PartSize += Texs[i]->GetDeviceDataSize(); } if (!Texs[i]->IsStreamed()) { NonStrSize += Texs[i]->GetDataSize(); } } sprintf_s(buf, "Current tex. objects: %d (Size: %.3fMb, Dyn. Atlases: %.3f, Dyn. Separated: %.3f, Loaded: %.3f, NonStreamed: %.3f)", Texs.Num(), Size / (1024.0f * 1024.0f), SizeDynAtl / (1024.0f * 1024.0f), SizeDynCom / (1024.0f * 1024.0f), PartSize / (1024.0f * 1024.0f), NonStrSize / (1024.0f * 1024.0f)); rd->TextToScreenColor(4, 27, 1, 0, 0, 1, buf); } #endif } } void CTexture::RT_LoadingUpdate() { CTexture::s_bStreamingFromHDD = gEnv->pSystem->GetStreamEngine()->IsStreamDataOnHDD(); CTexture::s_nStatsStreamPoolInUseMem = CTexture::s_pPoolMgr->GetInUseSize(); ITextureStreamer::EApplyScheduleFlags asf = CTexture::s_bPrecachePhase ? ITextureStreamer::eASF_InOut // Exclude the prep update, as it will be done by the RLT (and can be expensive) : ITextureStreamer::eASF_Full; s_pTextureStreamer->ApplySchedule(asf); } void CTexture::RLT_LoadingUpdate() { AZ_TRACE_METHOD(); s_pTextureStreamer->BeginUpdateSchedule(); } Ang3 sDeltAngles(Ang3& Ang0, Ang3& Ang1) { Ang3 out; for (int i = 0; i < 3; i++) { float a0 = Ang0[i]; a0 = (float)((360.0 / 65536) * ((int)(a0 * (65536 / 360.0)) & 65535)); // angmod float a1 = Ang1[i]; a1 = (float)((360.0 / 65536) * ((int)(a0 * (65536 / 360.0)) & 65535)); out[i] = a0 - a1; } return out; } SEnvTexture* CTexture::FindSuitableEnvTex(Vec3& Pos, Ang3& Angs, bool bMustExist, int RendFlags, bool bUseExistingREs, CShader* pSH, CShaderResources* pRes, CRenderObject* pObj, bool bReflect, IRenderElement* pRE, bool* bMustUpdate) { SEnvTexture* cm = NULL; float time0 = iTimer->GetAsyncCurTime(); int i; float distO = 999999; float adist = 999999; int firstForUse = -1; int firstFree = -1; Vec3 objPos; if (bMustUpdate) { *bMustUpdate = false; } if (!pObj) { bReflect = false; } else { if (bReflect) { Plane pl; pRE->mfGetPlane(pl); objPos = pl.MirrorPosition(Vec3(0, 0, 0)); } else if (pRE) { pRE->mfCenter(objPos, pObj); } else { objPos = pObj->GetTranslation(); } } float dist = 999999; for (i = 0; i < MAX_ENVTEXTURES; i++) { SEnvTexture* cur = &CTexture::s_EnvTexts[i]; if (cur->m_bReflected != bReflect) { continue; } float s = (cur->m_CamPos - Pos).GetLengthSquared(); Ang3 angDelta = sDeltAngles(Angs, cur->m_Angle); float a = angDelta.x * angDelta.x + angDelta.y * angDelta.y + angDelta.z * angDelta.z; float so = 0; if (bReflect) { so = (cur->m_ObjPos - objPos).GetLengthSquared(); } if (s <= dist && a <= adist && so <= distO) { dist = s; adist = a; distO = so; firstForUse = i; if (!so && !s && !a) { break; } } if (cur->m_pTex && !cur->m_pTex->m_pTexture && firstFree < 0) { firstFree = i; } } if (bMustExist && firstForUse >= 0) { return &CTexture::s_EnvTexts[firstForUse]; } if (bReflect) { dist = distO; } float curTime = iTimer->GetCurrTime(); int nUpdate = -2; float fTimeInterval = dist * CRenderer::CV_r_envtexupdateinterval + CRenderer::CV_r_envtexupdateinterval * 0.5f; float fDelta = curTime - CTexture::s_EnvTexts[firstForUse].m_TimeLastUpdated; if (bMustExist) { nUpdate = -2; } else if (dist > MAX_ENVTEXSCANDIST) { if (firstFree >= 0) { nUpdate = firstFree; } else { nUpdate = -1; } } else if (fDelta > fTimeInterval) { nUpdate = firstForUse; } if (nUpdate == -2) { // No need to update (Up to date) return &CTexture::s_EnvTexts[firstForUse]; } if (!CTexture::s_EnvTexts[nUpdate].m_pTex) { return NULL; } if (nUpdate >= 0) { if (!CTexture::s_EnvTexts[nUpdate].m_pTex->m_pTexture || gRenDev->m_RP.m_PS[gRenDev->m_RP.m_nProcessThreadID].m_fEnvTextUpdateTime < 0.1f) { int n = nUpdate; CTexture::s_EnvTexts[n].m_TimeLastUpdated = curTime; CTexture::s_EnvTexts[n].m_CamPos = Pos; CTexture::s_EnvTexts[n].m_Angle = Angs; CTexture::s_EnvTexts[n].m_ObjPos = objPos; CTexture::s_EnvTexts[n].m_bReflected = bReflect; if (bMustUpdate) { *bMustUpdate = true; } } gRenDev->m_RP.m_PS[gRenDev->m_RP.m_nProcessThreadID].m_fEnvTextUpdateTime += iTimer->GetAsyncCurTime() - time0; return &CTexture::s_EnvTexts[nUpdate]; } dist = 0; firstForUse = -1; for (i = 0; i < MAX_ENVTEXTURES; i++) { SEnvTexture* cur = &CTexture::s_EnvTexts[i]; if (dist < curTime - cur->m_TimeLastUpdated && !cur->m_bInprogress) { dist = curTime - cur->m_TimeLastUpdated; firstForUse = i; } } if (firstForUse < 0) { return NULL; } int n = firstForUse; CTexture::s_EnvTexts[n].m_TimeLastUpdated = curTime; CTexture::s_EnvTexts[n].m_CamPos = Pos; CTexture::s_EnvTexts[n].m_ObjPos = objPos; CTexture::s_EnvTexts[n].m_Angle = Angs; CTexture::s_EnvTexts[n].m_bReflected = bReflect; if (bMustUpdate) { *bMustUpdate = true; } gRenDev->m_RP.m_PS[gRenDev->m_RP.m_nProcessThreadID].m_fEnvTextUpdateTime += iTimer->GetAsyncCurTime() - time0; return &CTexture::s_EnvTexts[n]; } //=========================================================================== void CTexture::ShutDown() { uint32 i; if (gRenDev->GetRenderType() == eRT_Null) // workaround to fix crash when quitting the dedicated server - because the textures are shared { return; // should be fixed soon } RT_FlushAllStreamingTasks(true); ReleaseSystemTextures(); if (CRenderer::CV_r_releaseallresourcesonexit) { SResourceContainer* pRL = CBaseResource::GetResourcesForClass(CTexture::mfGetClassName()); if (pRL) { int n = 0; ResourcesMapItor itor; for (itor = pRL->m_RMap.begin(); itor != pRL->m_RMap.end(); ) { CTexture* pTX = (CTexture*)itor->second; itor++; if (!pTX) { continue; } if (CRenderer::CV_r_printmemoryleaks) { iLog->Log("Warning: CTexture::ShutDown: Texture %s was not deleted (%d)", pTX->GetName(), pTX->GetRefCounter()); } SAFE_RELEASE_FORCE(pTX); n++; } } } if (s_ShaderTemplatesInitialized) { for (i = 0; i < EFTT_MAX; i++) { (*s_ShaderTemplates)[i].~CTexture(); } } s_ShaderTemplates->Free(); SAFE_DELETE(s_pTexNULL); s_pPoolMgr->Flush(); } bool CTexture::ReloadFile_Request(const char* szFileName) { s_xTexReloadLock.Lock(); s_vTexReloadRequests.insert(szFileName); s_xTexReloadLock.Unlock(); return true; } bool CTexture::ReloadFile(const char* szFileName) { char realNameBuffer[256]; fpConvertDOSToUnixName(realNameBuffer, szFileName); char* realName = realNameBuffer; bool bStatus = false; SResourceContainer* pRL = CBaseResource::GetResourcesForClass(CTexture::mfGetClassName()); if (pRL) { AUTO_LOCK(CBaseResource::s_cResLock); AZStd::string normalizedFile; AZStd::string fileExtension; AzFramework::StringFunc::Path::GetExtension(szFileName, fileExtension); if (szFileName[0] == '$' || fileExtension.empty()) { //If the name starts with $ or it does not have any extension then it is one of the special texture that the engine requires and we would not be modifying the name normalizedFile = szFileName; } else { char buffer[AZ_MAX_PATH_LEN]; IResourceCompilerHelper::GetOutputFilename(szFileName, buffer, sizeof(buffer)); // change texture filename extensions to dds normalizedFile = buffer; AZStd::to_lower(normalizedFile.begin(), normalizedFile.end()); PathUtil::ToUnixPath(normalizedFile.c_str()); } CCryNameTSCRC Name = GenName(normalizedFile.c_str()); ResourcesMapItor itor = pRL->m_RMap.find(Name); if (itor != pRL->m_RMap.end()) { CTexture* tp = (CTexture*)itor->second; if (tp->Reload()) { bStatus = true; } } // Since we do not have the information whether the modified file was also reloaded with the FT_ALPHA flag we will try to reload that as well Name = GenName(normalizedFile.c_str(), FT_ALPHA); itor = pRL->m_RMap.find(Name); if (itor != pRL->m_RMap.end()) { CTexture* tp = (CTexture*)itor->second; if (tp->Reload()) { bStatus = true; } } } return bStatus; } void CTexture::ReloadTextures() { SResourceContainer* pRL = CBaseResource::GetResourcesForClass(CTexture::mfGetClassName()); if (pRL) { ResourcesMapItor itor; int nID = 0; for (itor = pRL->m_RMap.begin(); itor != pRL->m_RMap.end(); itor++, nID++) { CTexture* tp = (CTexture*)itor->second; if (!tp) { continue; } if (!(tp->m_nFlags & FT_FROMIMAGE)) { continue; } tp->Reload(); } } } bool CTexture::SetNoTexture( const CTexture* pDefaultTexture /* = "NoTexture" entry */) { if (pDefaultTexture) { m_pDevTexture = pDefaultTexture->m_pDevTexture; m_pDeviceShaderResource = pDefaultTexture->m_pDeviceShaderResource; m_eTFSrc = pDefaultTexture->GetSrcFormat(); m_eTFDst = pDefaultTexture->GetDstFormat(); m_nMips = pDefaultTexture->GetNumMips(); m_nWidth = pDefaultTexture->GetWidth(); m_nHeight = pDefaultTexture->GetHeight(); m_nDepth = 1; m_nDefState = pDefaultTexture->m_nDefState; m_fAvgBrightness = 1.0f; m_cMinColor = 0.0f; m_cMaxColor = 1.0f; m_cClearColor = ColorF(0.0f, 0.0f, 0.0f, 1.0f); m_bNoTexture = true; if (m_pFileTexMips) { Unlink(); StreamState_ReleaseInfo(this, m_pFileTexMips); m_pFileTexMips = NULL; } m_bStreamed = false; m_bPostponed = false; m_nFlags |= FT_FAILED; m_nActualSize = 0; m_nPersistentSize = 0; return true; } return false; } //=========================================================================== void CTexture::ReleaseSystemTextures() { if (s_pStatsTexWantedLists) { for (int i = 0; i < 2; ++i) { s_pStatsTexWantedLists[i].clear(); } } SAFE_RELEASE_FORCE(s_ptexRT_2D); SAFE_RELEASE_FORCE(s_ptexCloudsLM); SAFE_RELEASE_FORCE(s_ptexVolumetricFog); SAFE_RELEASE_FORCE(s_ptexVolumetricFogDensityColor); SAFE_RELEASE_FORCE(s_ptexVolumetricFogDensity); SAFE_RELEASE_FORCE(s_ptexVolumetricClipVolumeStencil); uint32 i; for (i = 0; i < 8; i++) { SAFE_RELEASE_FORCE(s_ptexShadowID[i]); } SAFE_RELEASE_FORCE(s_ptexFromObj); SAFE_RELEASE_FORCE(s_ptexSvoTree); SAFE_RELEASE_FORCE(s_ptexSvoTris); SAFE_RELEASE_FORCE(s_ptexSvoGlobalCM); SAFE_RELEASE_FORCE(s_ptexSvoRgbs); SAFE_RELEASE_FORCE(s_ptexSvoNorm); SAFE_RELEASE_FORCE(s_ptexSvoOpac); SAFE_RELEASE_FORCE(s_ptexFromObjCM); SAFE_RELEASE_FORCE(s_ptexVolObj_Density); SAFE_RELEASE_FORCE(s_ptexVolObj_Shadow); SAFE_RELEASE_FORCE(s_ptexColorChart); for (i = 0; i < MAX_ENVCUBEMAPS; i++) { s_EnvCMaps[i].Release(); } for (i = 0; i < MAX_ENVTEXTURES; i++) { s_EnvTexts[i].Release(); } SAFE_RELEASE_FORCE(s_ptexMipColors_Diffuse); SAFE_RELEASE_FORCE(s_ptexMipColors_Bump); SAFE_RELEASE_FORCE(s_ptexSkyDomeMie); SAFE_RELEASE_FORCE(s_ptexSkyDomeRayleigh); SAFE_RELEASE_FORCE(s_ptexSkyDomeMoon); SAFE_RELEASE_FORCE(s_ptexRT_ShadowPool); SAFE_RELEASE_FORCE(s_ptexRT_ShadowStub); SAFE_RELEASE_FORCE(s_ptexSceneNormalsMapMS); SAFE_RELEASE_FORCE(s_ptexSceneDiffuseAccMapMS); SAFE_RELEASE_FORCE(s_ptexSceneSpecularAccMapMS); SAFE_RELEASE_FORCE(s_defaultEnvironmentProbeDummy); s_CustomRT_2D->Free(); s_pPoolMgr->Flush(); // release targets pools SDynTexture::ShutDown(); SDynTexture2::ShutDown(); ReleaseMiscTargets(); m_bLoadedSystem = false; } void CTexture::LoadDefaultSystemTextures() { LOADING_TIME_PROFILE_SECTION; #if !defined(NULL_RENDERER) char str[256]; int i; if (!m_bLoadedSystem) { m_bLoadedSystem = true; // Default Template textures int nRTFlags = FT_DONT_RELEASE | FT_DONT_STREAM | FT_STATE_CLAMP | FT_USAGE_RENDERTARGET; s_ptexMipColors_Diffuse = CTexture::CreateTextureObject("$MipColors_Diffuse", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_MIPCOLORS_DIFFUSE); s_ptexMipColors_Bump = CTexture::CreateTextureObject("$MipColors_Bump", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_MIPCOLORS_BUMP); s_ptexRT_2D = CTexture::CreateTextureObject("$RT_2D", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_RT_2D); s_ptexRainOcclusion = CTexture::CreateTextureObject("$RainOcclusion", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown); s_ptexRainSSOcclusion[0] = CTexture::CreateTextureObject("$RainSSOcclusion0", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown); s_ptexRainSSOcclusion[1] = CTexture::CreateTextureObject("$RainSSOcclusion1", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown); s_ptexFromObj = CTexture::CreateTextureObject("FromObj", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_FROMOBJ); s_ptexSvoTree = CTexture::CreateTextureObject("SvoTree", 0, 0, 1, eTT_3D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SVOTREE); s_ptexSvoTris = CTexture::CreateTextureObject("SvoTris", 0, 0, 1, eTT_3D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SVOTRIS); s_ptexSvoGlobalCM = CTexture::CreateTextureObject("SvoGlobalCM", 0, 0, 1, eTT_Cube, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SVOGLCM); s_ptexSvoRgbs = CTexture::CreateTextureObject("SvoRgbs", 0, 0, 1, eTT_3D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SVORGBS); s_ptexSvoNorm = CTexture::CreateTextureObject("SvoNorm", 0, 0, 1, eTT_3D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SVONORM); s_ptexSvoOpac = CTexture::CreateTextureObject("SvoOpac", 0, 0, 1, eTT_3D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SVOOPAC); s_ptexFromObjCM = CTexture::CreateTextureObject("$FromObjCM", 0, 0, 1, eTT_Cube, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_FROMOBJ_CM); s_ptexZTargetDownSample[0] = CTexture::CreateTextureObject("$ZTargetDownSample0", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM, eTF_Unknown); s_ptexZTargetDownSample[1] = CTexture::CreateTextureObject("$ZTargetDownSample1", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM, eTF_Unknown); s_ptexZTargetDownSample[2] = CTexture::CreateTextureObject("$ZTargetDownSample2", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM, eTF_Unknown); s_ptexZTargetDownSample[3] = CTexture::CreateTextureObject("$ZTargetDownSample3", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM, eTF_Unknown); s_ptexSceneNormalsMapMS = CTexture::CreateTextureObject("$SceneNormalsMapMS", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SCENE_NORMALMAP_MS); s_ptexSceneDiffuseAccMapMS = CTexture::CreateTextureObject("$SceneDiffuseAccMS", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SCENE_DIFFUSE_ACC_MS); s_ptexSceneSpecularAccMapMS = CTexture::CreateTextureObject("$SceneSpecularAccMS", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SCENE_SPECULAR_ACC_MS); s_ptexSceneNormalsMapMS = CTexture::CreateTextureObject("$SceneNormalsMapMS", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SCENE_NORMALMAP_MS); s_ptexSceneDiffuseAccMapMS = CTexture::CreateTextureObject("$SceneDiffuseAccMS", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SCENE_DIFFUSE_ACC_MS); s_ptexSceneSpecularAccMapMS = CTexture::CreateTextureObject("$SceneSpecularAccMS", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SCENE_SPECULAR_ACC_MS); #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION TEXTURE_CPP_SECTION_4 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/Texture_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/Texture_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/Texture_cpp_salem.inl" #endif #endif s_ptexRT_ShadowPool = CTexture::CreateTextureObject("$RT_ShadowPool", 0, 0, 1, eTT_2D, FT_DONT_STREAM | FT_USAGE_RENDERTARGET | FT_USAGE_DEPTHSTENCIL, eTF_Unknown); s_ptexRT_ShadowStub = CTexture::CreateTextureObject("$RT_ShadowStub", 0, 0, 1, eTT_2D, FT_DONT_STREAM | FT_USAGE_RENDERTARGET | FT_USAGE_DEPTHSTENCIL, eTF_Unknown); s_ptexDepthBufferQuarter = CTexture::CreateTextureObject("$DepthBufferQuarter", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET | FT_USAGE_DEPTHSTENCIL, eTF_Unknown); if (!s_ptexModelHudBuffer) { s_ptexModelHudBuffer = CTexture::CreateTextureObject("$ModelHud", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_MODELHUD); } if (!s_ptexBackBuffer) { s_ptexSceneTarget = CTexture::CreateTextureObject("$SceneTarget", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SCENE_TARGET); s_ptexCurrSceneTarget = s_ptexSceneTarget; s_ptexSceneTargetR11G11B10F[0] = CTexture::CreateTextureObject("$SceneTargetR11G11B10F_0", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); s_ptexSceneTargetR11G11B10F[1] = CTexture::CreateTextureObject("$SceneTargetR11G11B10F_1", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); s_ptexSceneTargetScaledR11G11B10F[0] = CTexture::CreateTextureObject("$SceneTargetScaled0R11G11B10F", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); s_ptexSceneTargetScaledR11G11B10F[1] = CTexture::CreateTextureObject("$SceneTargetScaled1R11G11B10F", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); s_ptexSceneTargetScaledR11G11B10F[2] = CTexture::CreateTextureObject("$SceneTargetScaled2R11G11B10F", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); s_ptexSceneTargetScaledR11G11B10F[3] = CTexture::CreateTextureObject("$SceneTargetScaled3R11G11B10F", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); s_ptexVelocity = CTexture::CreateTextureObject("$Velocity", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); s_ptexVelocityTiles[0] = CTexture::CreateTextureObject("$VelocityTilesTmp0", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); s_ptexVelocityTiles[1] = CTexture::CreateTextureObject("$VelocityTilesTmp1", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); s_ptexVelocityTiles[2] = CTexture::CreateTextureObject("$VelocityTiles", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); s_ptexVelocityObjects[0] = CTexture::CreateTextureObject("$VelocityObjects", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); if (gRenDev->m_bDualStereoSupport) { s_ptexVelocityObjects[1] = CTexture::CreateTextureObject("$VelocityObjects_R", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); } #if defined(OPENGL_ES) || defined(CRY_USE_METAL) if (gcpRendD3D && gcpRendD3D->FX_GetEnabledGmemPath(nullptr)) { s_ptexGmemStenLinDepth = CTexture::CreateTextureObject("$GmemStenLinDepth", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); } #endif s_ptexBackBuffer = CTexture::CreateTextureObject("$BackBuffer", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_BACKBUFFERMAP); s_ptexPrevFrameScaled = CTexture::CreateTextureObject("$PrevFrameScale", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown); s_ptexWaterRipplesDDN = CTexture::CreateTextureObject("$WaterRipplesDDN_0", 256, 256, 1, eTT_2D, /*FT_DONT_RELEASE |*/ FT_DONT_STREAM | FT_USAGE_RENDERTARGET | FT_FORCE_MIPS, eTF_Unknown, TO_WATERRIPPLESMAP); s_ptexBackBufferScaled[0] = CTexture::CreateTextureObject("$BackBufferScaled_d2", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_BACKBUFFERSCALED_D2); s_ptexBackBufferScaled[1] = CTexture::CreateTextureObject("$BackBufferScaled_d4", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_BACKBUFFERSCALED_D4); s_ptexBackBufferScaled[2] = CTexture::CreateTextureObject("$BackBufferScaled_d8", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_BACKBUFFERSCALED_D8); s_ptexBackBufferScaledTemp[0] = CTexture::CreateTextureObject("$BackBufferScaledTemp_d2", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); s_ptexBackBufferScaledTemp[1] = CTexture::CreateTextureObject("$BackBufferScaledTemp_d4", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, -1); s_ptexSceneNormalsMap = CTexture::CreateTextureObject("$SceneNormalsMap", 0, 0, 1, eTT_2D, nRTFlags, eTF_R8G8B8A8, TO_SCENE_NORMALMAP); s_ptexSceneNormalsBent = CTexture::CreateTextureObject("$SceneNormalsBent", 0, 0, 1, eTT_2D, nRTFlags, eTF_R8G8B8A8); s_ptexSceneDiffuse = CTexture::CreateTextureObject("$SceneDiffuse", 0, 0, 1, eTT_2D, nRTFlags, eTF_R8G8B8A8); ETEX_Format rtTextureFormat = eTF_R8G8B8A8; // Slimming GBuffer requires only one channel for specular due to packing of RGB values into YPbPr and // specular components into less channels, thus saving bandwidth by not including G,B,A channels (75% saving) if(CRenderer::CV_r_SlimGBuffer == 1) { rtTextureFormat = eTF_R8; } s_ptexSceneSpecular = CTexture::CreateTextureObject("$SceneSpecular", 0, 0, 1, eTT_2D, nRTFlags, rtTextureFormat); #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION TEXTURE_CPP_SECTION_5 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/Texture_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/Texture_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/Texture_cpp_salem.inl" #endif #endif #if defined(AZ_PLATFORM_IOS) int nRTSceneDiffuseFlags = nRTFlags; static ICVar* pVar = gEnv->pConsole->GetCVar("e_ShadowsClearShowMaskAtLoad"); if (pVar && !pVar->GetIVal()) { nRTSceneDiffuseFlags |= FT_USAGE_MEMORYLESS; } s_ptexSceneDiffuseAccMap = CTexture::CreateTextureObject("$SceneDiffuseAcc", 0, 0, 1, eTT_2D, nRTSceneDiffuseFlags, eTF_R8G8B8A8, TO_SCENE_DIFFUSE_ACC); #else s_ptexSceneDiffuseAccMap = CTexture::CreateTextureObject("$SceneDiffuseAcc", 0, 0, 1, eTT_2D, nRTFlags, eTF_R8G8B8A8, TO_SCENE_DIFFUSE_ACC); #endif s_ptexSceneSpecularAccMap = CTexture::CreateTextureObject("$SceneSpecularAcc", 0, 0, 1, eTT_2D, nRTFlags, eTF_R8G8B8A8, TO_SCENE_SPECULAR_ACC); s_ptexAmbientLookup = CTexture::CreateTextureObject("$AmbientLookup", 0, 0, 1, eTT_2D, nRTFlags, eTF_R8G8B8A8); s_ptexShadowMask = CTexture::CreateTextureObject("$ShadowMask", 0, 0, 1, eTT_2D, nRTFlags, eTF_R8G8B8A8, TO_SHADOWMASK); s_ptexFlaresGather = CTexture::CreateTextureObject("$FlaresGather", 0, 0, 1, eTT_2D, nRTFlags, eTF_R8G8B8A8); for (i = 0; i < MAX_OCCLUSION_READBACK_TEXTURES; i++) { azsprintf(str, "$FlaresOcclusion_%d", i); s_ptexFlaresOcclusionRing[i] = CTexture::CreateTextureObject(str, 0, 0, 1, eTT_2D, nRTFlags, eTF_R8G8B8A8); } // fixme: get texture resolution from CREWaterOcean uint32 waterOceanMapFlags = FT_DONT_RELEASE | FT_NOMIPS | FT_USAGE_DYNAMIC | FT_DONT_STREAM; uint32 waterVolumeTempFlags = FT_NOMIPS | FT_USAGE_DYNAMIC | FT_DONT_STREAM; #if CRY_USE_METAL //We are now using the GPU to copy data into this texture. As a result we need to tag this texture as MtlTextureUsageRenderTarget so that on Metal can set the appropriate Usage flag. waterOceanMapFlags |= FT_USAGE_RENDERTARGET; waterVolumeTempFlags |= FT_USAGE_RENDERTARGET; #endif s_ptexWaterOcean = CTexture::CreateTextureObject("$WaterOceanMap", 64, 64, 1, eTT_2D, waterOceanMapFlags, eTF_Unknown, TO_WATEROCEANMAP); s_ptexWaterVolumeTemp = CTexture::CreateTextureObject("$WaterVolumeTemp", 64, 64, 1, eTT_2D, waterVolumeTempFlags, eTF_Unknown); s_ptexWaterVolumeDDN = CTexture::CreateTextureObject("$WaterVolumeDDN", 64, 64, 1, eTT_2D, /*FT_DONT_RELEASE |*/ FT_DONT_STREAM | FT_USAGE_RENDERTARGET | FT_FORCE_MIPS, eTF_Unknown, TO_WATERVOLUMEMAP); s_ptexWaterVolumeRefl[0] = CTexture::CreateTextureObject("$WaterVolumeRefl", 64, 64, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET | FT_FORCE_MIPS, eTF_Unknown, TO_WATERVOLUMEREFLMAP); s_ptexWaterVolumeRefl[1] = CTexture::CreateTextureObject("$WaterVolumeReflPrev", 64, 64, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET | FT_FORCE_MIPS, eTF_Unknown, TO_WATERVOLUMEREFLMAPPREV); s_ptexWaterCaustics[0] = CTexture::CreateTextureObject("$WaterVolumeCaustics", 512, 512, 1, eTT_2D, /*FT_DONT_RELEASE |*/ FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_WATERVOLUMECAUSTICSMAP); s_ptexWaterCaustics[1] = CTexture::CreateTextureObject("$WaterVolumeCausticsTemp", 512, 512, 1, eTT_2D, /*FT_DONT_RELEASE |*/ FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_WATERVOLUMECAUSTICSMAPTEMP); s_ptexRainDropsRT[0] = CTexture::CreateTextureObject("$RainDropsAccumRT_0", 512, 512, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown); s_ptexRainDropsRT[1] = CTexture::CreateTextureObject("$RainDropsAccumRT_1", 512, 512, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown); if (!s_ptexZTarget) { //for d3d10 we cannot free it during level transition, therefore allocate once and keep it #if defined(OPENGL_ES) || defined(CRY_USE_METAL) // Custom Z-Target for GMEM render path if (gcpRendD3D && gcpRendD3D->FX_GetEnabledGmemPath(nullptr)) { s_ptexZTarget = CTexture::s_ptexGmemStenLinDepth; } else #endif { s_ptexZTarget = CTexture::CreateTextureObject("$ZTarget", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown); } } s_ptexFurZTarget = CTexture::CreateTextureObject("$FurZTarget", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown); s_ptexZTargetScaled = CTexture::CreateTextureObject("$ZTargetScaled", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_DOWNSCALED_ZTARGET_FOR_AO); s_ptexZTargetScaled2 = CTexture::CreateTextureObject("$ZTargetScaled2", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_QUARTER_ZTARGET_FOR_AO); } #if defined(OPENGL_ES) || defined(CRY_USE_METAL) // GMEM render path uses CTexture::s_ptexSceneSpecularAccMap as the HDR Target // It gets set in CDeferredShading::CreateDeferredMaps() if (gcpRendD3D && !gcpRendD3D->FX_GetEnabledGmemPath(nullptr)) { s_ptexHDRTarget = CTexture::CreateTextureObject("$HDRTarget", 0, 0, 1, eTT_2D, nRTFlags, eTF_Unknown); } #elif defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION TEXTURE_CPP_SECTION_6 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/Texture_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/Texture_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/Texture_cpp_salem.inl" #endif #endif // Create dummy texture object for terrain and clouds lightmap s_ptexCloudsLM = CTexture::CreateTextureObject("$CloudsLM", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_CLOUDS_LM); for (i = 0; i < 8; i++) { azsprintf(str, "$FromRE_%d", i); if (!s_ptexFromRE[i]) { s_ptexFromRE[i] = CTexture::CreateTextureObject(str, 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_FROMRE0 + i); } } for (i = 0; i < 8; i++) { azsprintf(str, "$ShadowID_%d", i); s_ptexShadowID[i] = CTexture::CreateTextureObject(str, 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SHADOWID0 + i); } for (i = 0; i < 2; i++) { azsprintf(str, "$FromRE%d_FromContainer", i); if (!s_ptexFromRE_FromContainer[i]) { s_ptexFromRE_FromContainer[i] = CTexture::CreateTextureObject(str, 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_FROMRE0_FROM_CONTAINER + i); } } s_ptexVolObj_Density = CTexture::CreateTextureObject("$VolObj_Density", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_VOLOBJ_DENSITY); s_ptexVolObj_Shadow = CTexture::CreateTextureObject("$VolObj_Shadow", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_VOLOBJ_SHADOW); s_ptexColorChart = CTexture::CreateTextureObject("$ColorChart", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_COLORCHART); s_ptexSkyDomeMie = CTexture::CreateTextureObject("$SkyDomeMie", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SKYDOME_MIE); s_ptexSkyDomeRayleigh = CTexture::CreateTextureObject("$SkyDomeRayleigh", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SKYDOME_RAYLEIGH); s_ptexSkyDomeMoon = CTexture::CreateTextureObject("$SkyDomeMoon", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET, eTF_Unknown, TO_SKYDOME_MOON); for (i = 0; i < EFTT_MAX; i++) { ::new(&((*s_ShaderTemplates)[i]))CTexture(FT_DONT_RELEASE); (*s_ShaderTemplates)[i].SetCustomID(EFTT_DIFFUSE + i); (*s_ShaderTemplates)[i].SetFlags(FT_DONT_RELEASE); } s_ShaderTemplatesInitialized = true; s_pTexNULL = new CTexture(FT_DONT_RELEASE); s_ptexVolumetricFog = CTexture::CreateTextureObject("$VolumetricInscattering", 0, 0, 0, eTT_3D, FT_NOMIPS | FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_UNORDERED_ACCESS, eTF_Unknown); s_ptexVolumetricFogDensityColor = CTexture::CreateTextureObject("$DensityColorVolume", 0, 0, 0, eTT_3D, FT_NOMIPS | FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET | FT_USAGE_UNORDERED_ACCESS, eTF_Unknown); s_ptexVolumetricFogDensity = CTexture::CreateTextureObject("$DensityVolume", 0, 0, 0, eTT_3D, FT_NOMIPS | FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_RENDERTARGET | FT_USAGE_UNORDERED_ACCESS, eTF_Unknown); s_ptexVolumetricClipVolumeStencil = CTexture::CreateTextureObject("$ClipVolumeStencilVolume", 0, 0, 0, eTT_2D, FT_NOMIPS | FT_DONT_RELEASE | FT_DONT_STREAM | FT_USAGE_DEPTHSTENCIL | FT_USAGE_RENDERTARGET, eTF_Unknown); // Create dummy texture object the "default environment probe". This is only used for forward rendered passes that do not currently support tiled lighting. // This texture object is solely used for the association between the string "$DefaultEnvironmentProbe" and the enum TO_DEFAULT_ENVIRONMENT_PROBE if (!s_defaultEnvironmentProbeDummy) { s_defaultEnvironmentProbeDummy = CTexture::CreateTextureObject("$DefaultEnvironmentProbe", 0, 0, 1, eTT_2D, FT_DONT_RELEASE | FT_DONT_STREAM, eTF_Unknown, TO_DEFAULT_ENVIRONMENT_PROBE); } #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION TEXTURE_CPP_SECTION_7 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/Texture_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/Texture_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/Texture_cpp_salem.inl" #endif #endif } #endif } ////////////////////////////////////////////////////////////////////////// const char* CTexture::GetFormatName() const { return NameForTextureFormat(GetDstFormat()); } ////////////////////////////////////////////////////////////////////////// const char* CTexture::GetTypeName() const { return NameForTextureType(GetTextureType()); } ////////////////////////////////////////////////////////////////////////// void CRenderer::EF_AddRTStat(CTexture* pTex, int nFlags, int nW, int nH) { SRTargetStat TS; int nSize; ETEX_Format eTF; if (!pTex) { eTF = eTF_R8G8B8A8; if (nW < 0) { nW = m_width; } if (nH < 0) { nH = m_height; } nSize = CTexture::TextureDataSize(nW, nH, 1, 1, 1, eTF); TS.m_Name = "Back buffer"; } else { eTF = pTex->GetDstFormat(); if (nW < 0) { nW = pTex->GetWidth(); } if (nH < 0) { nH = pTex->GetHeight(); } nSize = CTexture::TextureDataSize(nW, nH, 1, pTex->GetNumMips(), 1, eTF); const char* szName = pTex->GetName(); if (szName && szName[0] == '$') { TS.m_Name = string("@") + string(&szName[1]); } else { TS.m_Name = szName; } } TS.m_eTF = eTF; if (nFlags > 0) { if (nFlags == 1) { TS.m_Name += " (Target)"; } else if (nFlags == 2) { TS.m_Name += " (Depth)"; nSize = nW * nH * 3; } else if (nFlags == 4) { TS.m_Name += " (Stencil)"; nSize = nW * nH; } else if (nFlags == 3) { TS.m_Name += " (Target + Depth)"; nSize += nW * nH * 3; } else if (nFlags == 6) { TS.m_Name += " (Depth + Stencil)"; nSize = nW * nH * 4; } else if (nFlags == 5) { TS.m_Name += " (Target + Stencil)"; nSize += nW * nH; } else if (nFlags == 7) { TS.m_Name += " (Target + Depth + Stencil)"; nSize += nW * nH * 4; } else { assert(0); } } TS.m_nSize = nSize; TS.m_nWidth = nW; TS.m_nHeight = nH; m_RP.m_RTStats.push_back(TS); } void CRenderer::EF_PrintRTStats(const char* szName) { const int nYstep = 14; int nY = 30; // initial Y pos int nX = 20; // initial X pos ColorF col = Col_Green; Draw2dLabel((float)nX, (float)nY, 1.6f, &col.r, false, szName); nX += 10; nY += 25; col = Col_White; int nYstart = nY; int nSize = 0; for (int i = 0; i < m_RP.m_RTStats.size(); i++) { SRTargetStat* pRT = &m_RP.m_RTStats[i]; Draw2dLabel((float)nX, (float)nY, 1.4f, &col.r, false, "%s (%d x %d x %s), Size: %.3f Mb", pRT->m_Name.c_str(), pRT->m_nWidth, pRT->m_nHeight, CTexture::NameForTextureFormat(pRT->m_eTF), (float)pRT->m_nSize / 1024.0f / 1024.0f); nY += nYstep; if (nY >= m_height - 25) { nY = nYstart; nX += 500; } nSize += pRT->m_nSize; } col = Col_Yellow; Draw2dLabel((float)nX, (float)(nY + 10), 1.4f, &col.r, false, "Total: %d RT's, Size: %.3f Mb", m_RP.m_RTStats.size(), nSize / 1024.0f / 1024.0f); } bool CTexture::IsMSAAChanged() { const RenderTargetData* pRtdt = m_pRenderTargetData; #if defined(NULL_RENDERER) return false; #else return pRtdt && (pRtdt->m_nMSAASamples != gRenDev->m_RP.m_MSAAData.Type || pRtdt->m_nMSAAQuality != gRenDev->m_RP.m_MSAAData.Quality); #endif } STexPool::~STexPool() { STexPoolItemHdr* pITH = m_ItemsList.m_Next; while (pITH != &m_ItemsList) { STexPoolItemHdr* pNext = pITH->m_Next; STexPoolItem* pIT = static_cast<STexPoolItem*>(pITH); CryLogAlways("***** Texture %p (%s) still in pool %p! Memory leak and crash will follow *****\n", pIT->m_pTex, pIT->m_pTex ? pIT->m_pTex->GetName() : "NULL", this); if (pIT->m_pTex) { pIT->m_pTex->ReleaseDeviceTexture(true); // Try to recover in release } *const_cast<STexPool**>(&pIT->m_pOwner) = NULL; pITH = pNext; } } const ETEX_Type CTexture::GetTextureType() const { return m_eTT; } void CTexture::SetTextureType(ETEX_Type type) { // Only set the type if we have not loaded the file and created the device // texture if (!m_pDevTexture) { m_eTT = type; } } const int CTexture::GetTextureID() const { return GetID(); } #ifdef TEXTURE_GET_SYSTEM_COPY_SUPPORT const ColorB* CTexture::GetLowResSystemCopy(uint16& nWidth, uint16& nHeight, int** ppLowResSystemCopyAtlasId) { const LowResSystemCopyType::iterator& it = s_LowResSystemCopy.find(this); if (it != CTexture::s_LowResSystemCopy.end()) { nWidth = (*it).second.m_nLowResCopyWidth; nHeight = (*it).second.m_nLowResCopyHeight; *ppLowResSystemCopyAtlasId = &(*it).second.m_nLowResSystemCopyAtlasId; return (*it).second.m_lowResSystemCopy.GetElements(); } return NULL; } void CTexture::PrepareLowResSystemCopy(const byte* pTexData, bool bTexDataHasAllMips) { if (m_eTT != eTT_2D || (m_nMips <= 1 && (m_nWidth > 16 || m_nHeight > 16))) { return; } // this function handles only compressed textures for now if (m_eTFDst != eTF_BC3 && m_eTFDst != eTF_BC1 && m_eTFDst != eTF_BC2) { return; } // make sure we skip non diffuse textures if (strstr(GetName(), "_ddn") || strstr(GetName(), "_ddna") || strstr(GetName(), "_mask") || strstr(GetName(), "_spec.") || strstr(GetName(), "_gloss") || strstr(GetName(), "_displ") || strstr(GetName(), "characters") || strstr(GetName(), "$") ) { return; } if (pTexData) { SLowResSystemCopy& rSysCopy = s_LowResSystemCopy[this]; rSysCopy.m_nLowResCopyWidth = m_nWidth; rSysCopy.m_nLowResCopyHeight = m_nHeight; int nSrcOffset = 0; int nMipId = 0; while ((rSysCopy.m_nLowResCopyWidth > 16 || rSysCopy.m_nLowResCopyHeight > 16 || nMipId < 2) && (rSysCopy.m_nLowResCopyWidth >= 8 && rSysCopy.m_nLowResCopyHeight >= 8)) { nSrcOffset += TextureDataSize(rSysCopy.m_nLowResCopyWidth, rSysCopy.m_nLowResCopyHeight, 1, 1, 1, m_eTFDst); rSysCopy.m_nLowResCopyWidth /= 2; rSysCopy.m_nLowResCopyHeight /= 2; nMipId++; } int nSizeDxtMip = TextureDataSize(rSysCopy.m_nLowResCopyWidth, rSysCopy.m_nLowResCopyHeight, 1, 1, 1, m_eTFDst); int nSizeRgbaMip = TextureDataSize(rSysCopy.m_nLowResCopyWidth, rSysCopy.m_nLowResCopyHeight, 1, 1, 1, eTF_R8G8B8A8); rSysCopy.m_lowResSystemCopy.CheckAllocated(nSizeRgbaMip / sizeof(ColorB)); gRenDev->DXTDecompress(pTexData + (bTexDataHasAllMips ? nSrcOffset : 0), nSizeDxtMip, (byte*)rSysCopy.m_lowResSystemCopy.GetElements(), rSysCopy.m_nLowResCopyWidth, rSysCopy.m_nLowResCopyHeight, 1, m_eTFDst, false, 4); } } #endif // TEXTURE_GET_SYSTEM_COPY_SUPPORT void CTexture::InvalidateDeviceResource(uint32 dirtyFlags) { //In the editor, multiple worker threads could destroy device resource sets //which point to this texture. We need to lock to avoid a race-condition. AZStd::lock_guard<AZStd::mutex> lockGuard(*m_invalidateCallbacksMutex); for (const auto& cb : m_invalidateCallbacks) { cb.second(dirtyFlags); } } void CTexture::AddInvalidateCallback(void* listener, InvalidateCallbackType callback) { //In the editor, multiple worker threads could destroy device resource sets //which point to this texture. We need to lock to avoid a race-condition. AZStd::lock_guard<AZStd::mutex> lockGuard(*m_invalidateCallbacksMutex); m_invalidateCallbacks.insert(AZStd::pair<void*, InvalidateCallbackType>(listener, callback)); } void CTexture::RemoveInvalidateCallbacks(void* listener) { //In the editor, multiple worker threads could destroy device resource sets //which point to this texture. We need to lock to avoid a race-condition. AZStd::lock_guard<AZStd::mutex> lockGuard(*m_invalidateCallbacksMutex); m_invalidateCallbacks.erase(listener); } void CTexture::ApplyDepthTextureState(int unit, int nFilter, bool clamp) { if (s_ptexZTarget != nullptr) { STexState depthTextState(nFilter, clamp); s_ptexZTarget->Apply(unit, GetTexState(depthTextState)); } } CTexture* CTexture::GetZTargetTexture() { return s_ptexZTarget; } int CTexture::GetTextureState(const STexState& TS) { return GetTexState(TS); } void CTexture::ApplyForID(int id, int nTUnit, int nTState, int nTexMaterialSlot, int nSUnit, bool useWhiteDefault) { CTexture* pTex = id > 0 ? CTexture::GetByID(id) : nullptr; if (pTex) { pTex->Apply(nTUnit, nTState, nTexMaterialSlot, nSUnit); } else if (useWhiteDefault) { CTextureManager::Instance()->GetWhiteTexture()->Apply(nTUnit, nTState, nTexMaterialSlot, nSUnit); } } CTexAnim::CTexAnim() { m_nRefCount = 1; m_Rand = 0; m_NumAnimTexs = 0; m_bLoop = false; m_Time = 0.0f; } CTexAnim::~CTexAnim() { for (uint32 i = 0; i < m_TexPics.Num(); i++) { ITexture* pTex = (ITexture*)m_TexPics[i]; SAFE_RELEASE(pTex); } m_TexPics.Free(); } void CTexAnim::AddRef() { CryInterlockedIncrement(&m_nRefCount); } void CTexAnim::Release() { long refCnt = CryInterlockedDecrement(&m_nRefCount); if (refCnt > 0) { return; } delete this; } int CTexAnim::Size() const { int nSize = sizeof(CTexAnim); nSize += m_TexPics.GetMemoryUsage(); return nSize; }