#include "InstancedEntityRenderer.h" //Game Includes #include "renderer/RenderPhases/RenderPhaseWaterReflection.h" #include "shaders/CustomShaderEffectGrass.h" #include "scene/lod/LodDrawable.h" #include "camera/CamInterface.h" #include "bank/bank.h" RENDER_OPTIMISATIONS() PARAM(instancebatchincapture,"Show useful batch info in GPAD/PIX"); // // Statics // BankBool InstancedRendererConfig::sRenderInstancedGeometry = true; BankBool InstancedRendererConfig::sAddCSEToDrawlist = true; BankBool InstancedRendererConfig::sCSESetBatchRenderStates = true; BankBool InstancedRendererConfig::sAddPixBatchAnnotation = false; //Should be false by default! Otherwise, we'll do unnecessary work for instanced geometry. There's a cmdline option to enable by default. //Cross Fade debugging BankBool InstancedRendererConfig::sRenderNonCrossFadingGeometry = true; BankBool InstancedRendererConfig::sRenderFadingOutGeometry = true; BankBool InstancedRendererConfig::sRenderFadingInGeometry = true; //LOD debugging BANK_ONLY(atRangeArray InstancedRendererConfig::sRenderLodGeometry(true)); BankBool InstancedRendererConfig::sOutputGbufferInstancedDrawList1Frame = false; BankBool InstancedRendererConfig::sOutputGbufferInstancedBatches1Frame = false; BankBool sClearCrossFadeOnMinMaxStippleMask = true; #define INSTANCE_BUFFER_PREALLOCATE_AMT 1500 namespace InstancedDrawHelpers { //TODO: This is a slightly modified copy of the same func from DrawableDataStructs.cpp. Should probably merge them at some point. static u32 AdjustAlpha(u32 alpha, bool shouldForceAlpha) { bool isWaterReflectionDrawList; if(sysThreadType::IsRenderThread()) { isWaterReflectionDrawList = gDrawListMgr->IsExecutingDrawList(DL_RENDERPHASE_WATER_REFLECTION); } else { isWaterReflectionDrawList = gDrawListMgr->IsBuildingDrawList(DL_RENDERPHASE_WATER_REFLECTION); } // Usual case is not shouldForceAlpha == false, so check that first so we // can avoid both the call to IsExecutingDrawList() and the branch midprediction. if (!shouldForceAlpha || !isWaterReflectionDrawList) { return alpha; } // this causes SLOD3's etc. to cover up the LODs unfortunately, but // without it we can't render lower LODs in place of higher LODs .. return LODTYPES_ALPHA_VISIBLE; } } // // InstancedEntityCache // InstancedEntityCache::InstancedEntityCache() : mEntity(NULL) , mDrawHandler(NULL) , mLODlevel(0) , mCrossFade(-1) , mStippleMask(0xFFFF) , mIsLODModelInstanced(false) , mIsNextLODModelInstanced(false) , mLODModelHasNonInstancedGeom(false) , mNextLODModelHasNonInstancedGeom(false) , mSetStipple(false) { } InstancedEntityCache::InstancedEntityCache(CEntity *entity, fwDrawData *drawHandler) : mEntity(entity) , mDrawHandler(drawHandler) , mLODlevel(0) , mCrossFade(-1) , mStippleMask(0xFFFF) , mIsLODModelInstanced(false) , mIsNextLODModelInstanced(false) , mLODModelHasNonInstancedGeom(false) , mNextLODModelHasNonInstancedGeom(false) , mSetStipple(false) { RecomputeLODLevel(); } void InstancedEntityCache::SetEntity(CEntity *entity, fwDrawData *drawHandler) { mEntity = entity; mDrawHandler = drawHandler; mIsLODModelInstanced = mIsNextLODModelInstanced = mLODModelHasNonInstancedGeom = mNextLODModelHasNonInstancedGeom = false; mSetStipple = false; mStippleMask = 0xFFFF; RecomputeLODLevel(); } void InstancedEntityCache::RecomputeLODLevel() { //Cull trees intersecting the script vehicle hack... if(mDrawHandler && mDrawHandler->GetShaderEffect() && mDrawHandler->GetShaderEffect()->GetType() == CSE_TREE && CCustomShaderEffectGrass::IntersectsScriptFlattenAABB(mEntity)) mEntity = NULL; //Culled entity, make cache entry invalid so this won't be added to instanced draw cache. if(mDrawHandler == NULL) mEntity = NULL; //Make this invalid if either entity or drawHandler is NULL. This way, IsValid just has to check the entity ptr. if(IsValid()) { if(CBaseModelInfo *pModelInfo = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(GetModelIndex())))) { if(rmcDrawable *pDrawable = pModelInfo->GetDrawable()) { //Vars from entity u32 fadeAlpha = mEntity->GetAlpha(); bool forceAlpha = CRenderPhaseWaterReflectionInterface::ShouldEntityForceUpAlpha(mEntity); bool useAltfadeDist = mEntity->GetUseAltFadeDistance(); LODDrawable::crossFadeDistanceIdx fadeDist = useAltfadeDist ? LODDrawable::CFDIST_ALTERNATE : LODDrawable::CFDIST_MAIN; u32 phaseLODs = mEntity->GetPhaseLODs(); u32 lastLODIdx = mEntity->GetLastLODIdx(); mActualAlphaFade = InstancedDrawHelpers::AdjustAlpha(fadeAlpha, forceAlpha); //const eRenderMode renderMode = DRAWLISTMGR->GetRtRenderMode(); //const u32 bucketMask = DRAWLISTMGR->GetRtBucketMask(); const bool isShadowDrawList = static_cast(gDrawListMgr)->IsBuildingShadowDrawList(); const bool isGBufferDrawList = static_cast(gDrawListMgr)->IsBuildingGBufDrawList(); mLODlevel = 0; mCrossFade = -1; u32 LODFlag = (phaseLODs >> dlCmdNewDrawList::GetCurrDrawListLODFlagShift_UpdateThread()) & LODDrawable::LODFLAG_MASK; if (LODFlag != LODDrawable::LODFLAG_NONE) { if (LODFlag & LODDrawable::LODFLAG_FORCE_LOD_LEVEL) { mLODlevel = LODFlag & ~LODDrawable::LODFLAG_FORCE_LOD_LEVEL; } else { //float dist = LODDrawable::CalcLODDistance(VEC3V_TO_VECTOR3(mEntity->GetTransform().GetPosition())); float dist = Dist(VECTOR3_TO_VEC3V(camInterface::GetPos()), mEntity->GetTransform().GetPosition()).Getf(); LODDrawable::CalcLODLevelAndCrossfadeForProxyLod(pDrawable, dist, LODFlag, mLODlevel, mCrossFade, fadeDist, lastLODIdx); } } else { //float dist = LODDrawable::CalcLODDistance(VEC3V_TO_VECTOR3(mEntity->GetTransform().GetPosition())); float dist = Dist(VECTOR3_TO_VEC3V(camInterface::GetPos()), mEntity->GetTransform().GetPosition()).Getf(); LODDrawable::CalcLODLevelAndCrossfade(pDrawable, dist, mLODlevel, mCrossFade, fadeDist, lastLODIdx ASSERT_ONLY(, pModelInfo)); } if((LODFlag != LODDrawable::LODFLAG_NONE || isShadowDrawList) && mCrossFade > -1) { if(mCrossFade < 128 && mLODlevel + 1 < LOD_COUNT && pDrawable->GetLodGroup().ContainsLod(mLODlevel + 1)) mLODlevel = mLODlevel + 1; mCrossFade = -1; } else if( mActualAlphaFade < 255 ) // Entity + cross fade fade: we can't deal with this, nuke crossFade { mCrossFade = -1; } //These checks happens in rendering code... so simplify it and do it here. if(mLODlevel >= 3 || !pDrawable->GetLodGroup().ContainsLod(mLODlevel + 1)) { mCrossFade = -1; } //Check current lod if it has instanced geometry. bool hasNonInstShaders; mIsLODModelInstanced = pDrawable->GetLodGroup().IsLodInstanced(mLODlevel, pDrawable->GetShaderGroup(), hasNonInstShaders); mLODModelHasNonInstancedGeom = hasNonInstShaders; //If crossfading, check if the next lod has instanced geometry. if(mCrossFade != -1) { mIsNextLODModelInstanced = pDrawable->GetLodGroup().IsLodInstanced(mLODlevel + 1, pDrawable->GetShaderGroup(), hasNonInstShaders); mNextLODModelHasNonInstancedGeom = hasNonInstShaders; } //const bool allowAlphaBlend = !mEntity->IsBaseFlagSet(fwEntity::HAS_OPAQUE) && mEntity->IsBaseFlagSet(fwEntity::HAS_DECAL); u32 stippleAlpha = GetStippleAlpha(); mSetStipple = IsCrossFading() ? isGBufferDrawList : false; //isGBufferDrawList && !allowAlphaBlend; mStippleMask = mSetStipple ? CShaderLib::GetStippleMask(stippleAlpha, false) : 0xFFFF; //Slight optimization for stippled alpha with stipple mask = 0xFFFF or 0x0000 if(mSetStipple && sClearCrossFadeOnMinMaxStippleMask) { if(mStippleMask == 0xFFFF && CShaderLib::GetStippleMask(stippleAlpha, true) == 0x0000) { mSetStipple = false; mCrossFade = -1; } else if(mStippleMask == 0x000 && CShaderLib::GetStippleMask(stippleAlpha, true) == 0xFFFF) { mSetStipple = false; mCrossFade = -1; mLODlevel = mLODlevel + 1; } } } // if(rmcDrawable *pDrawable = pModelInfo->GetDrawable()) } //if(CBaseModelInfo *pModelInfo = CModelInfo::GetBaseModelInfo(fwModelId(static_cast(GetModelIndex())))) } //IsValid() } bool InstancedEntityCache::OnlyNeedsInstancedRenderer() const { //How should we deal with cross-fading entities where 1 lod is instanced and 1 is not? What about geometry with both //instanced and non-instanced geometry? //While not quite optimal, we can prompt the calling system to submit the entity for normal drawing as well. bool onlyNeedsInstRender = IsCurrLODInstanced() && !mLODModelHasNonInstancedGeom && (!IsCrossFading() || (IsCurrLODInstanced() && !mNextLODModelHasNonInstancedGeom)); // if(IsCrossFading()) // onlyNeedsInstRender = onlyNeedsInstRender && cachedEntity.IsCurrLODInstanced() && !mNextLODModelHasNonInstancedGeom; return onlyNeedsInstRender; } //Instanced batch pix tagging void InstancedEntityCache::PushPixTagsForBatch(u32 PIX_TAGGING_ONLY(modelIndex), u32 PIX_TAGGING_ONLY(batchSize)) { //Don't do extra tagging work if not necessary!! #if ENABLE_PIX_TAGGING char batchName[255]; fwModelId modelId((strLocalIndex(modelIndex))); CBaseModelInfo *pModelInfo = CModelInfo::GetBaseModelInfo(modelId); sprintf(batchName, "%s Batch - %d Instances", pModelInfo ? pModelInfo->GetModelName() : "", batchSize); PF_PUSH_MARKER(batchName); #else PF_PUSH_MARKER("Instanced Batch"); #endif } void InstancedEntityCache::PopPixTagsForBatch() { PF_POP_MARKER(); } // // InstancedRendererConfig // void InstancedRendererConfig::Init() { #if RSG_PC // increase size of pool static bool g_firstFrame = true; if ( g_firstFrame ){ const int PreAllocateSize = INSTANCE_BUFFER_PREALLOCATE_AMT; for(int i=0;i