2775 lines
107 KiB
C++
2775 lines
107 KiB
C++
//
|
|
// entity/entitybatch.cpp : base class for batched entity lists
|
|
//
|
|
// Copyright (C) 1999-2014 Rockstar Games. All Rights Reserved.
|
|
//
|
|
|
|
#include <algorithm>
|
|
#include <numeric>
|
|
//#include <limits>
|
|
//#include <type_traits>
|
|
|
|
//Rage Includes
|
|
#include "diag/art_channel.h"
|
|
#include "grcore/instancebuffer.h"
|
|
#include "grcore/device.h"
|
|
#include "grcore/wrapper_d3d.h"
|
|
#include "grcore/wrapper_gnm.h"
|
|
#include "grcore/effect.h"
|
|
#include "grmodel/modelfactory.h"
|
|
#include "fwmaths/vectorutil.h"
|
|
#include "fwrenderer/instancing.h" //Really just for constant buffers
|
|
#include "fwscene/mapdata/mapdata.h"
|
|
#include "fwscene/mapdata/mapinstancedata.h"
|
|
#include "fwscene/stores/mapdatastore.h"
|
|
#include "fwscene/stores/drawablestore.h"
|
|
#include "fwsys/gameskeleton.h"
|
|
#include "fwsys/timer.h"
|
|
#include "fwdebug/picker.h"
|
|
#include "bank/bank.h"
|
|
#include "bank/combo.h"
|
|
#include "grcore/debugdraw.h"
|
|
#include "math/amath.h"
|
|
#include "system/stl_wrapper.h"
|
|
|
|
#if RSG_ORBIS
|
|
#include "grcore/gfxcontext_gnm.h"
|
|
#endif
|
|
|
|
#include "EntityBatch.h"
|
|
|
|
//Game Includes
|
|
#include "camera/CamInterface.h"
|
|
#include "physics/WorldProbe/shapetestprobedesc.h"
|
|
#include "renderer/DrawLists/drawListMgr.h"
|
|
#include "renderer/Entities/EntityBatchDrawHandler.h"
|
|
#include "renderer/RenderPhases/RenderPhaseCascadeShadows.h"
|
|
#include "renderer/RenderPhases/RenderPhaseWaterReflection.h" // for WATER_REFLECTION_PRE_REFLECTED define ..
|
|
#include "renderer/render_channel.h"
|
|
#include "scene/lod/LodDrawable.h"
|
|
#include "scene/lod/LodScale.h"
|
|
#include "scene/world/VisibilityMasks.h"
|
|
#include "shaders/ShaderLib.h"
|
|
#include "Shaders/CustomShaderEffectGrass.h"
|
|
#include "timecycle/TimeCycle.h"
|
|
|
|
//Debug Only
|
|
#include "task/System/AsyncProbeHelper.h"
|
|
#include "scene/portals/FrustumDebug.h"
|
|
|
|
#if KEEP_INSTANCELIST_ASSETS_RESIDENT
|
|
#include "fwscene/stores/maptypesstore.h"
|
|
#endif //KEEP_INSTANCELIST_ASSETS_RESIDENT
|
|
|
|
#if __D3D11 && __WIN32PC
|
|
#include <d3d11.h>
|
|
#endif //__D3D11 && __WIN32PC
|
|
|
|
#if NV_SUPPORT
|
|
#include "../../3rdParty/NVidia/nvapi.h"
|
|
#endif
|
|
|
|
SCENE_OPTIMISATIONS()
|
|
|
|
FW_INSTANTIATE_CLASS_POOL(CEntityBatch, CONFIGURED_FROM_FILE, atHashString("EntityBatch",0x5817ce07));
|
|
FW_INSTANTIATE_CLASS_POOL(CGrassBatch, CONFIGURED_FROM_FILE, atHashString("GrassBatch",0xe0b50aa6));
|
|
|
|
#if GRASS_BATCH_CS_CULLING
|
|
PARAM(disableGrassComputeShader, "Disable running the compute shader on the grass (renders all visible grass batches with no fading)");
|
|
PARAM(grassDisableRenderGeometry, "Disables the rendering of the grass geometry only (still runs the CS computation)");
|
|
#endif // GRASS_BATCH_CS_CULLING
|
|
|
|
namespace EBStatic
|
|
{
|
|
CSettings::eSettingsLevel sQuality = CSettings::High;
|
|
|
|
#if RSG_DURANGO || RSG_PC
|
|
u32 sPerfSkipInstance = RSG_DURANGO ? 0x01 : 0x0f; // Default to 'all batches' for PC.
|
|
bool bPerfForceSkipInstance = false;
|
|
#endif // RSG_DURANGO || RSG_PC...
|
|
|
|
static BankBool sDebugDrawEnabled = false;
|
|
static BankBool sDebugDrawBatchAABB = true;
|
|
static BankBool sDebugDrawInstances = false;
|
|
static BankBool sDebugDrawLinkedBounds = false;
|
|
|
|
static BankBool sDebugDrawLinkedRenderedInfo = true;
|
|
static BankBool sDebugDrawLinkedNotRenderedInfo = false;
|
|
|
|
static BankBool sComputeAoScalePerInstance = true;
|
|
static BankBool sOverrideLodDist = false;
|
|
static BankUInt32 sOverriddenLodDistValue = 0x1fff;
|
|
static BankBool sEnableLinkageVisibility = true;
|
|
static BankBool sResetLinkageVisibility = true;
|
|
static BankFloat sShadowLodFactor = 0.5f;
|
|
|
|
BankBool sPerfOnlyDraw1stInstanceGBuf = false;
|
|
BankBool sPerfOnlyDraw1stInstanceShadows = false;
|
|
GRASS_BATCH_CS_CULLING_ONLY(BankBool sGrassRenderGeometry = true);
|
|
GRASS_BATCH_CS_CULLING_ONLY(BankBool sPerfEnableComputeShader = true);
|
|
|
|
#if __BANK
|
|
static BankBool sOutputStaticInstanceBuffer = true;
|
|
static BankBool sOutputStaticIBData = false;
|
|
static BankBool sProbeForNormal = true; //Probes for actual terrain normal when reporting grass info
|
|
|
|
static BankBool sDebugDrawBatchAABBSolid = false;
|
|
static Color32 sDebugDrawBatchAABBColor(0.0f, 0.0f, 1.0f); //Blue
|
|
|
|
static BankBool sDebugDrawInstanceAABBSolid = false;
|
|
static Color32 sDebugDrawInstanceAABBColor(0.0f, 1.0f, 0.0f); //Green
|
|
static Color32 sDebugDrawRenderedHdAABBColor(0.0f, 1.0f, 1.0f);
|
|
static Color32 sDebugDrawNotRenderedHdAABBColor(1.0f, 0.0f, 1.0f);
|
|
|
|
static BankBool sDebugDrawEntityTransformAABB = false;
|
|
static BankBool sDebugDrawEntityTransformAABBSolid = false;
|
|
static Color32 sDebugDrawEntityTransformAABBColor(1.0f, 0.0f, 0.0f); //Red
|
|
|
|
//Debug Draw - Grass Terrain Normal
|
|
BankBool sDebugDrawGrassTerrainNormalEnabled = true;
|
|
BankFloat sDebugDrawGrassTerrainNormalLength = 1.1f;
|
|
BankFloat sDebugDrawGrassTerrainNormalConeLength = 0.3f;
|
|
BankFloat sDebugDrawGrassTerrainNormalConeRadius = 0.07f;
|
|
BankBool sDebugDrawGrassTerrainNormalConeCap = true;
|
|
Color32 sDebugDrawGrassTerrainNormalColor(0.0f, 0.0f, 1.0f); //Blue
|
|
|
|
static Color32 sDebugDrawSelectedTextColor(1.0f, 1.0f, 1.0f); //White
|
|
static Color32 sDebugDrawSelectedBatchAABBColor(1.0f, 1.0f, 0.0f); //Yellow
|
|
|
|
static Color32 sDebugDrawMapAABBLoadedColor(0.5f, 1.0f, 0.5f); //Green-ish
|
|
static Color32 sDebugDrawMapAABBUnloadedColor(1.0f, 0.5f, 0.5f); //Red-ish
|
|
|
|
bkBank *sBank = NULL;
|
|
bkGroup *sSelectionGroup = NULL;
|
|
bkGroup *sSelectedEntityGroup = NULL;
|
|
bkCombo *sMapNameComboWidget = NULL;
|
|
|
|
rage::atArray<s32> sMapDataDefIndex;
|
|
BankInt32 sSelectedMapData = -1;
|
|
BankInt32 sSelectedEntityBatch = -1;
|
|
BankInt32 sSelectedEntityBatchInst = -1;
|
|
|
|
//Performance & Usage Stats
|
|
BankBool sPerfDisplayUsageInfo = false;
|
|
BankBool sPerfDisplayRuntimeInfo = true;
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(BankBool sPerfDisplayAllocatorInfo = true);
|
|
BankBool sPerfDisplayPropUsageInfo = true;
|
|
BankBool sPerfDisplayGrassUsageInfo = true;
|
|
|
|
//Counters
|
|
u32 sPerfBatchesRenderedGbuf = 0;
|
|
u32 sPerfInstancesRenderedGbuf = 0;
|
|
u32 sPerfInstancesRenderedGbuf_LODFade = 0;
|
|
|
|
u32 sPerfBatchesRenderedShadow = 0;
|
|
u32 sPerfInstancesRenderedShadow = 0;
|
|
u32 sPerfInstancesRenderedShadow_LODFade = 0;
|
|
|
|
u32 sPerfBatchesRenderedOther = 0;
|
|
u32 sPerfInstancesRenderedOther = 0;
|
|
u32 sPerfInstancesRenderedOther_LODFade = 0;
|
|
|
|
#if GRASS_BATCH_CS_CULLING
|
|
BankBool sPerfDisplayGrassCSInfo = false && !__FINAL; //Preliminary... and potentially very slow!
|
|
# if DEVICE_GPU_WAIT
|
|
BankBool sPerfDisplayGrassCS_UseGPUFence = true;
|
|
# endif
|
|
u32 sPerfCSInstancesRenderedGbuf = 0;
|
|
u32 sPerfCSZeroCountBatchesGbuf = 0;
|
|
u32 sPerfCSInstancesRenderedShadow = 0;
|
|
u32 sPerfCSZeroCountBatchesShadow = 0;
|
|
u32 sPerfCSInstancesRenderedOther = 0;
|
|
u32 sPerfCSZeroCountBatchesOther = 0;
|
|
|
|
BankBool sGrassComputeShader_FreezeVp = false;
|
|
extern BankBool sGrassComputeShader_AllowBatching;
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(extern BankBool sGrassComputeShader_AllowCopyStructureCountBatching);
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(extern BankBool sGrassComputeShader_AvoidInterleaving);
|
|
extern BankBool sFlushCSAfterEachDispatch;
|
|
extern BankBool sCSDisableCrossfade;
|
|
ORBIS_ONLY(extern BankBool sGrassComputeShader_DebugDrawIndirect);
|
|
extern BankBool sGrassComputeShader_IgnoreLodScale_DrawableLOD;
|
|
extern BankBool sGrassComputeShader_IgnoreLodScale_LODFade;
|
|
extern BankBool sGrassComputeShader_UAV_Sync;
|
|
grcViewport sFrozenVp;
|
|
CFrustumDebug sFrustumDbg;
|
|
#endif //GRASS_BATCH_CS_CULLING
|
|
|
|
void InitMapDataDefIndexMap()
|
|
{
|
|
s32 storeSize = g_MapDataStore.GetSize();
|
|
for(int i = 0; i < storeSize; ++i)
|
|
{
|
|
fwMapDataDef *pDef = g_MapDataStore.GetSlot(strLocalIndex(i));
|
|
if(pDef && (pDef->GetContentFlags() & fwMapData::CONTENTFLAG_INSTANCE_LIST) > 0)
|
|
{
|
|
sMapDataDefIndex.PushAndGrow(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
fwEntity *GetSelectedEntity()
|
|
{
|
|
fwEntity *entity = NULL;
|
|
if(sSelectedMapData >= 0 && sSelectedEntityBatch >= 0)
|
|
{
|
|
fwMapDataDef *pDef = g_MapDataStore.GetSlot(strLocalIndex(sMapDataDefIndex[sSelectedMapData]));
|
|
if(pDef && pDef->IsLoaded() && pDef->GetContents() && pDef->GetContents()->GetLoaderInstRef().GetInstance())
|
|
{
|
|
fwMapDataContents *contents = pDef->GetContents();
|
|
fwInstancedMapData &instList = reinterpret_cast<fwMapData *>(contents->GetLoaderInstRef().GetInstance())->m_instancedData;
|
|
u32 numEntities = contents->GetNumEntities();
|
|
u32 instEntityOffset = numEntities - (instList.m_PropInstanceList.size() + instList.m_GrassInstanceList.size());
|
|
u32 ebIndex = instEntityOffset + sSelectedEntityBatch;
|
|
if(ebIndex < numEntities)
|
|
entity = contents->GetEntities()[ebIndex];
|
|
}
|
|
}
|
|
|
|
return entity;
|
|
}
|
|
|
|
int FindEBIndexInMap(fwEntity *entity, s32 mapDataDefIndex)
|
|
{
|
|
if(mapDataDefIndex < 0)
|
|
return -1;
|
|
|
|
fwMapDataDef *pDef = g_MapDataStore.GetSlot(strLocalIndex(mapDataDefIndex));
|
|
if(pDef && pDef->IsLoaded() && pDef->GetContents() && pDef->GetContents()->GetLoaderInstRef().GetInstance())
|
|
{
|
|
fwMapDataContents *contents = pDef->GetContents();
|
|
u32 numEntities = contents->GetNumEntities();
|
|
fwInstancedMapData &instList = reinterpret_cast<fwMapData *>(contents->GetLoaderInstRef().GetInstance())->m_instancedData;
|
|
u32 instEntityOffset = numEntities - (instList.m_PropInstanceList.size() + instList.m_GrassInstanceList.size());
|
|
|
|
for(int i = instEntityOffset; i < numEntities; ++i)
|
|
{
|
|
if(entity == contents->GetEntities()[i])
|
|
return i - instEntityOffset;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void OnSelectedEntityLodDistanceChanged()
|
|
{
|
|
if(CEntity *entity = static_cast<CEntity *>(GetSelectedEntity()))
|
|
{
|
|
if(entity->GetIsTypeInstanceList())
|
|
{
|
|
CEntityBatch *eb = static_cast<CEntityBatch *>(entity);
|
|
if(const CEntityBatch::InstanceList *list = eb->GetInstanceList())
|
|
{
|
|
eb->SetLodDistance(list->m_lodDist);
|
|
}
|
|
}
|
|
else if(entity->GetIsTypeGrassInstanceList())
|
|
{
|
|
CGrassBatch *eb = static_cast<CGrassBatch *>(entity);
|
|
if(const CGrassBatch::InstanceList *list = eb->GetInstanceList())
|
|
{
|
|
#if GRASS_BATCH_CS_CULLING
|
|
//Setup LOD distance - Must be large enough to cover LOD fade for farthest element.
|
|
ScalarV minLodDist = Mag(list->m_BatchAABB.GetExtent());
|
|
float lodDist = minLodDist.Getf() + list->m_lodDist;
|
|
#endif //#if GRASS_BATCH_CS_CULLING
|
|
eb->SetLodDistance(GRASS_BATCH_CS_CULLING_SWITCH(static_cast<u32>(lodDist), list->m_lodDist));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetSelectedEntityInPicker()
|
|
{
|
|
if(CEntity *entity = static_cast<CEntity *>(GetSelectedEntity()))
|
|
{
|
|
g_PickerManager.ResetList(true);
|
|
g_PickerManager.AddEntityToList(entity, false, false);
|
|
g_PickerManager.SetIndexOfSelectedEntity(0);
|
|
}
|
|
}
|
|
|
|
void OnBatchValuesChanged()
|
|
{
|
|
if(CEntity *entity = static_cast<CEntity *>(GetSelectedEntity()))
|
|
{
|
|
if(entity->GetIsTypeGrassInstanceList())
|
|
{
|
|
CGrassBatch *eb = static_cast<CGrassBatch *>(entity);
|
|
if(const CGrassBatch::InstanceList *list = eb->GetInstanceList())
|
|
{
|
|
if(fwDrawData *dh = eb->GetDrawHandlerPtr())
|
|
{
|
|
if(CCustomShaderEffectGrass *cse = static_cast<CCustomShaderEffectGrass *>(dh->GetShaderEffect()))
|
|
{
|
|
cse->SetScaleRange(list->m_ScaleRange);
|
|
cse->SetOrientToTerrain(list->m_OrientToTerrain);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateSelectedEntityBatchWidgets(bkBank &bank)
|
|
{
|
|
if(CEntity *entity = static_cast<CEntity *>(GetSelectedEntity()))
|
|
{
|
|
if(entity->GetIsTypeInstanceList())
|
|
{
|
|
CEntityBatch *eb = static_cast<CEntityBatch *>(entity);
|
|
if(const CEntityBatch::InstanceList *list = eb->GetInstanceList())
|
|
{
|
|
bank.AddText("Archetype Name", const_cast<atHashString *>(&(list->m_archetypeName)), true);
|
|
bank.AddSlider("LOD Distance", const_cast<u32 *>(&(list->m_lodDist)), 0u, 0x1FFFu, 1u, OnSelectedEntityLodDistanceChanged);
|
|
|
|
bank.AddSeparator("");
|
|
sSelectedEntityBatchInst = -1;
|
|
bank.AddSlider("Instance Index", &sSelectedEntityBatchInst, -1, list->m_InstanceList.size() - 1, 1);
|
|
}
|
|
}
|
|
else if(entity->GetIsTypeGrassInstanceList())
|
|
{
|
|
CGrassBatch *eb = static_cast<CGrassBatch *>(entity);
|
|
if(const CGrassBatch::InstanceList *list = eb->GetInstanceList())
|
|
{
|
|
bank.AddText("Archetype Name", const_cast<atHashString *>(&(list->m_archetypeName)), true);
|
|
bank.AddSlider("LOD Distance", const_cast<u32 *>(&(list->m_lodDist)), 0u, 0x1FFFu, 1u, OnSelectedEntityLodDistanceChanged);
|
|
bank.AddSeparator("");
|
|
bank.AddVector("Scale Range (Min|Max|Rand Scale)", const_cast<Vec3V *>(&(list->m_ScaleRange)), 0.0f, FLT_MAX / 1000.0f, 0.1f, OnBatchValuesChanged);
|
|
bank.AddSlider("Orient To Terrain", const_cast<float *>(&(list->m_OrientToTerrain)), 0.0f, 1.0f, 0.1f, OnBatchValuesChanged);
|
|
bank.AddSeparator("");
|
|
bank.AddSlider("LOD Fade Start Distance", const_cast<float *>(&(list->m_LodFadeStartDist)), 0.0f, static_cast<float>(0x1FFFu), 0.5f);
|
|
bank.AddSlider("LOD Instance Fade Range", const_cast<float *>(&(list->m_LodInstFadeRange)), 0.0f, 1.0f, 0.01f);
|
|
|
|
bank.AddSeparator("");
|
|
sSelectedEntityBatchInst = -1;
|
|
bank.AddSlider("Instance Index", &sSelectedEntityBatchInst, -1, list->m_InstanceList.size() - 1, 1);
|
|
}
|
|
}
|
|
|
|
bank.AddButton("Set Selected Entity To Picker", SetSelectedEntityInPicker);
|
|
}
|
|
}
|
|
|
|
void OnSelectedEntityBatchChanged();
|
|
void OnMapNameComboChanged()
|
|
{
|
|
while(bkWidget *widget = sMapNameComboWidget->GetNext())
|
|
widget->Destroy();
|
|
|
|
//Reset bank context and re-add widgets
|
|
if(sSelectedMapData >= 0)
|
|
{
|
|
sBank->SetCurrentGroup(*sSelectionGroup);
|
|
{
|
|
bkBank &bank = *sBank;
|
|
if(fwMapDataDef *pDef = g_MapDataStore.GetSlot(strLocalIndex(sMapDataDefIndex[sSelectedMapData])))
|
|
{
|
|
if(pDef->IsLoaded())
|
|
{
|
|
if(Verifyf(pDef->GetContents() && pDef->GetContents()->GetLoaderInstRef().GetInstance(), "MapDataDef Contents/MapData is NULL even though it's supposedly streamed in?"))
|
|
{
|
|
fwMapDataContents *contents = pDef->GetContents();
|
|
fwInstancedMapData &instList = reinterpret_cast<fwMapData *>(contents->GetLoaderInstRef().GetInstance())->m_instancedData; //contents->GetMapData()->m_instancedData;
|
|
s32 maxSelectedBatchIndex = instList.m_PropInstanceList.size() + instList.m_GrassInstanceList.size() - 1;
|
|
sSelectedEntityBatch = Clamp(sSelectedEntityBatch, -1, maxSelectedBatchIndex); //Make sure sSelectedEntityBatch is valid for newly selected map data
|
|
bank.AddSlider("Entity Batch", &sSelectedEntityBatch, -1, maxSelectedBatchIndex, 1, OnSelectedEntityBatchChanged); //datCallback(OnSelectedEntityBatchChanged));
|
|
sSelectedEntityGroup = bank.PushGroup("Selected EntityBatch");
|
|
UpdateSelectedEntityBatchWidgets(bank);
|
|
bank.PopGroup();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bank.AddTitle("");
|
|
char buf[256];
|
|
formatf(buf, "%s is not currently loaded!", g_MapDataStore.GetName(strLocalIndex(sMapDataDefIndex[sSelectedMapData])));
|
|
bank.AddTitle(buf);
|
|
|
|
//JKINZ
|
|
const spdAABB &aabb = fwMapDataStore::GetStore().GetStreamingBounds(strLocalIndex(sMapDataDefIndex[sSelectedMapData]));
|
|
bank.AddTitle("");
|
|
bank.AddTitle("Streaming extents:");
|
|
bank.AddTitle("Min = {%f, %f, %f}", aabb.GetMin().GetXf(), aabb.GetMin().GetYf(), aabb.GetMin().GetZf());
|
|
bank.AddTitle("Max = {%f, %f, %f}", aabb.GetMax().GetXf(), aabb.GetMax().GetYf(), aabb.GetMax().GetZf());
|
|
bank.AddTitle("Center = {%f, %f, %f}", aabb.GetCenter().GetXf(), aabb.GetCenter().GetYf(), aabb.GetCenter().GetZf());
|
|
|
|
bank.AddSeparator("");
|
|
bank.AddButton("Refresh Map Widgets", OnMapNameComboChanged);
|
|
}
|
|
}
|
|
}
|
|
sBank->UnSetCurrentGroup(*sSelectionGroup);
|
|
}
|
|
}
|
|
|
|
void OnSelectedEntityBatchChanged()
|
|
{
|
|
if(sSelectedMapData >= 0)
|
|
{
|
|
fwMapDataDef *pDef = g_MapDataStore.GetSlot(strLocalIndex(sMapDataDefIndex[sSelectedMapData]));
|
|
if(pDef && !pDef->IsLoaded())
|
|
OnMapNameComboChanged(); //Map streamed out, update widgets
|
|
else
|
|
{
|
|
while(bkWidget *widget = sSelectedEntityGroup->GetChild())
|
|
widget->Destroy();
|
|
|
|
sBank->SetCurrentGroup(*sSelectedEntityGroup);
|
|
UpdateSelectedEntityBatchWidgets(*sBank);
|
|
sBank->UnSetCurrentGroup(*sSelectedEntityGroup);
|
|
}
|
|
}
|
|
}
|
|
|
|
bkCombo *AddMapNameComboWidget(bkBank &bank)
|
|
{
|
|
static const char *sNoSelecitonName = ""; //1st element is an empty string that means no selection.
|
|
typedef rage::atArray<const char *> name_array;
|
|
name_array names(sMapDataDefIndex.size() + 1, sMapDataDefIndex.size() + 1);
|
|
|
|
//Populate names array
|
|
name_array::iterator iter = names.begin();
|
|
*iter = sNoSelecitonName;
|
|
std::transform(sMapDataDefIndex.begin(), sMapDataDefIndex.end(), ++iter, std::bind1st(std::mem_fun<const char *, const fwMapDataStore, int>(&fwMapDataStore::GetName), &g_MapDataStore));
|
|
|
|
return bank.AddCombo("Map Data", &sSelectedMapData, names.size(), names.GetElements(), -1, OnMapNameComboChanged);
|
|
}
|
|
|
|
void RefreshMapDataDefIndexMap()
|
|
{
|
|
sMapDataDefIndex.Reset();
|
|
InitMapDataDefIndexMap();
|
|
|
|
if(sMapNameComboWidget)
|
|
{
|
|
static const char *sNoSelecitonName = ""; //1st element is an empty string that means no selection.
|
|
typedef rage::atArray<const char *> name_array;
|
|
name_array names(sMapDataDefIndex.size() + 1, sMapDataDefIndex.size() + 1);
|
|
|
|
//Populate names array
|
|
name_array::iterator iter = names.begin();
|
|
*iter = sNoSelecitonName;
|
|
std::transform(sMapDataDefIndex.begin(), sMapDataDefIndex.end(), ++iter, std::bind1st(std::mem_fun<const char *, const fwMapDataStore, int>(&fwMapDataStore::GetName), &g_MapDataStore));
|
|
|
|
sMapNameComboWidget->UpdateCombo("Map Data", reinterpret_cast<void *>(&sSelectedMapData), names.size(), names.GetElements(), -1, OnMapNameComboChanged);
|
|
}
|
|
}
|
|
|
|
void OnSetSelectedEntityFromPicker()
|
|
{
|
|
if(fwEntity *entity = g_PickerManager.GetSelectedEntity())
|
|
{
|
|
int mapIndex = -1;
|
|
if(entity->GetType() == ENTITY_TYPE_INSTANCE_LIST) //CEntity::GetIsTypeInstanceList()
|
|
{
|
|
CEntityBatch *eb = static_cast<CEntityBatch *>(entity);
|
|
mapIndex = eb->GetMapDataDefIndex();
|
|
}
|
|
else if(entity->GetType() == ENTITY_TYPE_GRASS_INSTANCE_LIST) //CEntity::GetIsTypeGrassInstanceList()
|
|
{
|
|
CGrassBatch *eb = static_cast<CGrassBatch *>(entity);
|
|
mapIndex = eb->GetMapDataDefIndex();
|
|
}
|
|
else
|
|
{
|
|
return; //Not a batch entity
|
|
}
|
|
|
|
int batchIndex = FindEBIndexInMap(entity, mapIndex);
|
|
int remappedIndex = -1; //Find remapped map index
|
|
{
|
|
rage::atArray<s32>::iterator end = sMapDataDefIndex.end();
|
|
rage::atArray<s32>::iterator iter = std::find(sMapDataDefIndex.begin(), end, mapIndex);
|
|
if(iter == end)
|
|
{
|
|
RefreshMapDataDefIndexMap(); //Maybe array is stale?
|
|
iter = std::find(sMapDataDefIndex.begin(), end, mapIndex);
|
|
}
|
|
|
|
if(Verifyf(iter != end, "WARNING: MapData \"%s\" (index: %d) is not being found by Entity Batch Selection system!", g_MapDataStore.GetName(strLocalIndex(sMapDataDefIndex[sSelectedMapData])), mapIndex))
|
|
remappedIndex = static_cast<int>(iter - sMapDataDefIndex.begin());
|
|
}
|
|
|
|
if(remappedIndex >= 0 && batchIndex >= 0)
|
|
{
|
|
if(remappedIndex != sSelectedMapData)
|
|
{
|
|
sSelectedMapData = remappedIndex;
|
|
sSelectedEntityBatch = batchIndex;
|
|
OnMapNameComboChanged();
|
|
}
|
|
else if(batchIndex != sSelectedEntityBatch)
|
|
{
|
|
sSelectedEntityBatch = batchIndex;
|
|
OnSelectedEntityBatchChanged();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Update entity batch AO values
|
|
bool UpdateBatchAoScales(void *pItem, void *UNUSED_PARAM(data))
|
|
{
|
|
if(CEntity *entity = static_cast<CEntity *>(pItem))
|
|
entity->CalculateDynamicAmbientScales();
|
|
|
|
return true;
|
|
}
|
|
|
|
void UpdateAllBatchAoScales()
|
|
{
|
|
CEntityBatch::GetPool()->ForAll(EBStatic::UpdateBatchAoScales, NULL);
|
|
CGrassBatch::GetPool()->ForAll(EBStatic::UpdateBatchAoScales, NULL);
|
|
}
|
|
|
|
void DisplayStaticInstanceBuffer(fwPropInstanceListDef::IBList *OUTPUT_ONLY(ibList), const char *OUTPUT_ONLY(prefix), int OUTPUT_ONLY(batchIndex = -1))
|
|
{
|
|
#if !__NO_OUTPUT
|
|
if(ibList)
|
|
{
|
|
grcStaticInstanceBufferList &list = ibList->GetCurrentList();
|
|
grcInstanceBufferBasic *first = static_cast<grcInstanceBufferBasic *>(list.GetFirst());
|
|
|
|
u32 ibCount = 0;
|
|
for(grcInstanceBuffer *curr = first; curr; curr = curr->GetNext())
|
|
++ibCount;
|
|
|
|
Displayf( "%sIBList<%d>[%d]%s%3d: Count: %5d\tInst Per Draw: %3d\tlist: (grcStaticInstanceBufferList *)0x%p, first: (grcInstanceBufferBasic *)0x%p",
|
|
prefix, fwPropInstanceListDef::IBList::MaxFrames, grcInstanceBuffer::GetCurrentFrame(fwPropInstanceListDef::IBList::MaxFrames), (batchIndex >= 0 ? " " : ""), batchIndex,
|
|
ibCount, grcInstanceBuffer::MaxPerDraw, &list, first);
|
|
|
|
u32 ibIndex = 0;
|
|
for(grcInstanceBufferBasic *curr = first; curr; curr = static_cast<grcInstanceBufferBasic *>(curr->GetNext()))
|
|
{
|
|
//Displayf("%s\t%5d - (grcInstanceBufferBasic *)0x%p:\tCount: %2d\tElemSizeQW: %2d", prefix, ibIndex, curr, curr->GetCount(), curr->GetElemSizeQW());
|
|
Displayf("%s\t%5d - (grcInstanceBufferBasic *)0x%p:\tCount: %2zu\tElemSizeQW: %2zu\tData: (Vec4V *)0x%p", prefix, ibIndex, curr, curr->GetCount(), curr->GetElemSizeQW(), curr->GetData());
|
|
|
|
const Vec4V *data = reinterpret_cast<const Vec4V *>(curr->GetData());
|
|
if(sOutputStaticIBData && data)
|
|
{
|
|
for(int i = 0; i < curr->GetCount(); ++i)
|
|
{
|
|
for(int j = 0; j < curr->GetElemSizeQW(); ++j)
|
|
{
|
|
const Vec4V vec = data[(i * curr->GetElemSizeQW()) + j];
|
|
Displayf("%s\t\tinst[%d].Vec4V[%d]: {%.8f, %.8f, %.8f, %.8f}", prefix, i, j, vec.GetXf(), vec.GetYf(), vec.GetZf(), vec.GetWf());
|
|
}
|
|
}
|
|
}
|
|
|
|
++ibIndex;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void DisplayPropEntityInfo(const CEntityBatch *OUTPUT_ONLY(eb), const char *OUTPUT_ONLY(prefix), int OUTPUT_ONLY(batchIndex = -1))
|
|
{
|
|
#if !__NO_OUTPUT
|
|
if(eb && eb->GetInstanceList())
|
|
{
|
|
typedef CEntityBatch::InstanceList InstanceList;
|
|
const InstanceList *list = eb->GetInstanceList();
|
|
|
|
//Display batch header info:
|
|
atHashString imapLink;
|
|
fwMapDataDef *pDef = (eb->GetMapDataDefIndex() > -1 ? g_MapDataStore.GetSlot(strLocalIndex(eb->GetMapDataDefIndex())) : NULL);
|
|
if(pDef && pDef->GetContents() && pDef->GetContents()->GetLoaderInstRef().GetInstance())
|
|
{
|
|
fwMapDataContents *contents = pDef->GetContents();
|
|
fwInstancedMapData &instList = reinterpret_cast<fwMapData *>(contents->GetLoaderInstRef().GetInstance())->m_instancedData;
|
|
imapLink = instList.m_ImapLink;
|
|
}
|
|
const char *imapName = (imapLink.TryGetCStr() != NULL ? imapLink.TryGetCStr() : "<NULL>");
|
|
Displayf( "%sBatch%s%3d: Instance Count: %d\tArchetype: %s\tAABB [min|max] = {%f, %f, %f} | {%f, %f, %f}\tLOD Dist: %d (0x%x)\tLink Map Name|Index: %32s (0x%8x) | %5d",
|
|
prefix, (batchIndex >= 0 ? " " : ""), batchIndex,
|
|
list->m_InstanceList.size(), (list->m_archetypeName.TryGetCStr() ? list->m_archetypeName.TryGetCStr() : "<Unknown>"),
|
|
list->m_BatchAABB.GetMin().GetXf(), list->m_BatchAABB.GetMin().GetYf(), list->m_BatchAABB.GetMin().GetZf(),
|
|
list->m_BatchAABB.GetMax().GetXf(), list->m_BatchAABB.GetMax().GetYf(), list->m_BatchAABB.GetMax().GetZf(), list->m_lodDist, list->m_lodDist,
|
|
imapName, imapLink.GetHash(), eb->GetHdMapDataDefIndex() );
|
|
|
|
//fwMapDataDef *pDefHD = (eb->GetHdMapDataDefIndex() > -1 ? g_MapDataStore.GetSlot(eb->GetHdMapDataDefIndex()) : NULL);
|
|
InstanceList::InstanceDataList::const_iterator end = list->m_InstanceList.end();
|
|
InstanceList::InstanceDataList::const_iterator begin = list->m_InstanceList.begin();
|
|
InstanceList::InstanceDataList::const_iterator iter;
|
|
for(iter = begin; iter != end; ++iter)
|
|
{
|
|
Displayf( "%s\t%4d:\tPosition: {%5.6f, %5.6f, %5.6f}\tColor: {%3d, %3d, %3d, %3d}\tVisible: %-5s\t"
|
|
"Matrix: {%.8f, %.8f, %.8f} | {%.8f, %.8f, %.8f} | {%.8f, %.8f, %.8f}\tLink Entity Index: %5d\tGlobals: {%5.6f, %5.6f, %5.6f, %5.6f}",
|
|
prefix, (int)(iter - begin), iter->m_InstMat[0].GetWf(), iter->m_InstMat[1].GetWf(), iter->m_InstMat[2].GetWf(),
|
|
iter->m_Tint.GetRed(), iter->m_Tint.GetGreen(), iter->m_Tint.GetBlue(), iter->m_Tint.GetAlpha(), iter->m_IsVisible ? "true" : "false",
|
|
iter->m_InstMat[0].GetXf(), iter->m_InstMat[0].GetYf(), iter->m_InstMat[0].GetZf(),
|
|
iter->m_InstMat[1].GetXf(), iter->m_InstMat[1].GetYf(), iter->m_InstMat[1].GetZf(),
|
|
iter->m_InstMat[2].GetXf(), iter->m_InstMat[2].GetYf(), iter->m_InstMat[2].GetZf(),
|
|
iter->m_Index, iter->m_Globals.GetXf(), iter->m_Globals.GetYf(), iter->m_Globals.GetZf(), iter->m_Globals.GetWf() );
|
|
}
|
|
|
|
if(sOutputStaticInstanceBuffer)
|
|
{
|
|
Displayf("%s", prefix);
|
|
DisplayStaticInstanceBuffer(const_cast<fwPropInstanceListDef::IBList *>(list->GetStaticInstanceBufferList()), prefix, batchIndex);
|
|
Displayf("%s", prefix);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void DisplayGrassEntityInfo(const CGrassBatch *OUTPUT_ONLY(eb), const char *OUTPUT_ONLY(prefix), int OUTPUT_ONLY(batchIndex = -1))
|
|
{
|
|
#if !__NO_OUTPUT
|
|
if(eb && eb->GetInstanceList())
|
|
{
|
|
typedef CGrassBatch::InstanceList InstanceList;
|
|
const InstanceList *list = eb->GetInstanceList();
|
|
|
|
//Display batch header info:
|
|
Displayf( "%sBatch%s%3d: Instance Count: %d\tArchetype: %s\tAABB [min|max] = {%f, %f, %f} | {%f, %f, %f}\tLOD Dist: %d (0x%x)", prefix, (batchIndex >= 0 ? " " : ""),
|
|
batchIndex, list->m_InstanceList.size(), (list->m_archetypeName.TryGetCStr() ? list->m_archetypeName.TryGetCStr() : "<Unknown>"),
|
|
list->m_BatchAABB.GetMin().GetXf(), list->m_BatchAABB.GetMin().GetYf(), list->m_BatchAABB.GetMin().GetZf(),
|
|
list->m_BatchAABB.GetMax().GetXf(), list->m_BatchAABB.GetMax().GetYf(), list->m_BatchAABB.GetMax().GetZf(), list->m_lodDist, list->m_lodDist );
|
|
|
|
const Vec3V basePos = list->m_BatchAABB.GetMin();
|
|
const Vec3V offsetScale = list->m_BatchAABB.GetMax() - basePos;
|
|
|
|
InstanceList::InstanceDataList::const_iterator end = list->m_InstanceList.end();
|
|
InstanceList::InstanceDataList::const_iterator begin = list->m_InstanceList.begin();
|
|
InstanceList::InstanceDataList::const_iterator iter;
|
|
for(iter = begin; iter != end; ++iter)
|
|
{
|
|
//Compute position
|
|
typedef std::remove_reference<decltype(iter->m_Position[0])>::type pos_offset_type;
|
|
CompileTimeAssert(std::is_arithmetic<pos_offset_type>::value); //If this isn't the case, the line below will likely fail.
|
|
#if RSG_PC || RSG_DURANGO
|
|
static const pos_offset_type sMaxVal = static_cast<pos_offset_type>(-1);
|
|
#else //RSG_PC
|
|
static const auto sMaxVal = std::numeric_limits<pos_offset_type>::max();
|
|
#endif //RSG_PC
|
|
|
|
const ScalarV dist = ScalarV(static_cast<float>(sMaxVal));
|
|
const Vec3V offset = Vec3V(static_cast<float>(iter->m_Position[0]), static_cast<float>(iter->m_Position[1]), static_cast<float>(iter->m_Position[2])) / dist;
|
|
Vec3V position = basePos + (offset * offsetScale);
|
|
Vec3V normal = iter->ComputeNormal();
|
|
|
|
Vec3V vProbePos(V_ZERO);
|
|
Vec3V vProbeNorm(V_ZERO);
|
|
ScalarV vDot(V_NAN);
|
|
if(sProbeForNormal)
|
|
{
|
|
WorldProbe::CShapeTestProbeDesc probeDesc;
|
|
WorldProbe::CShapeTestHitPoint probeIsect;
|
|
WorldProbe::CShapeTestResults probeResult(probeIsect);
|
|
probeDesc.SetStartAndEnd(VEC3V_TO_VECTOR3(position + (Vec3V(V_UP_AXIS_WZERO) * ScalarV(V_QUARTER))), VEC3V_TO_VECTOR3(position + (Vec3V(V_UP_AXIS_WZERO) * ScalarV(V_NEGSIXTEEN))));
|
|
probeDesc.SetResultsStructure(&probeResult);
|
|
probeDesc.SetStateIncludeFlags(phLevelBase::STATE_FLAG_FIXED);
|
|
probeDesc.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
|
|
probeDesc.SetContext(WorldProbe::ENotSpecified);
|
|
|
|
if(WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc))
|
|
{
|
|
vProbePos = probeResult[0].GetHitPositionV();
|
|
vProbeNorm = probeResult[0].GetHitNormalV();
|
|
vDot = Dot(vProbeNorm, normal);
|
|
}
|
|
else
|
|
{
|
|
//Were we under the terrain? Try reversing the direction of the probe.
|
|
probeDesc.SetStartAndEnd(VEC3V_TO_VECTOR3(position - (Vec3V(V_UP_AXIS_WZERO) * ScalarV(V_QUARTER))), VEC3V_TO_VECTOR3(position + (Vec3V(V_UP_AXIS_WZERO) * ScalarV(V_FIFTEEN))));
|
|
if(WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc))
|
|
{
|
|
vProbePos = probeResult[0].GetHitPositionV();
|
|
vProbeNorm = probeResult[0].GetHitNormalV();
|
|
vDot = Dot(vProbeNorm, normal);
|
|
}
|
|
}
|
|
}
|
|
|
|
Displayf( "%s\t%4d:\tPosition [Offset|Final]: {%3d, %3d, %3d} | {%5.6f, %5.6f, %5.6f}\tColor: {%3d, %3d, %3d}\tNormal: {%5.6f, %5.6f, %5.6f}\tScale: %3d\tAo: %3d\t"
|
|
"ProbePos: {%5.6f, %5.6f, %5.6f}\tProbeNormal: {%5.6f, %5.6f, %5.6f}\tDot(Normal, ProbeNormal): %5.6f",
|
|
prefix, (int)(iter - begin), iter->m_Position[0], iter->m_Position[1], iter->m_Position[2], position.GetXf(), position.GetYf(), position.GetZf(),
|
|
iter->m_Color[0], iter->m_Color[1], iter->m_Color[2], normal.GetXf(), normal.GetYf(), normal.GetZf(), iter->m_Scale, iter->m_Ao,
|
|
vProbePos.GetXf(), vProbePos.GetYf(), vProbePos.GetZf(), vProbeNorm.GetXf(), vProbeNorm.GetYf(), vProbeNorm.GetZf(), vDot.Getf() );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void DisplaySelectedInfo()
|
|
{
|
|
if(CEntity *entity = static_cast<CEntity *>(GetSelectedEntity()))
|
|
{
|
|
if(entity->GetIsTypeInstanceList())
|
|
{
|
|
DisplayPropEntityInfo(static_cast<CEntityBatch *>(entity), "BATCH-DBG:\t", sSelectedEntityBatch);
|
|
}
|
|
else if(entity->GetIsTypeGrassInstanceList())
|
|
{
|
|
DisplayGrassEntityInfo(static_cast<CGrassBatch *>(entity), "GRASS-DBG:\t", sSelectedEntityBatch);
|
|
}
|
|
}
|
|
else if(sSelectedMapData >= 0)
|
|
{
|
|
//Output data for the whole map.
|
|
fwMapDataDef *pDef = g_MapDataStore.GetSlot(strLocalIndex(sMapDataDefIndex[sSelectedMapData]));
|
|
if(pDef && pDef->IsLoaded() && pDef->GetContents() && pDef->GetContents()->GetLoaderInstRef().GetInstance())
|
|
{
|
|
fwMapDataContents *contents = pDef->GetContents();
|
|
u32 numEntities = contents->GetNumEntities();
|
|
fwInstancedMapData &instList = reinterpret_cast<fwMapData *>(contents->GetLoaderInstRef().GetInstance())->m_instancedData;
|
|
u32 instEntityOffset = numEntities - (instList.m_PropInstanceList.size() + instList.m_GrassInstanceList.size());
|
|
u32 grassEntityOffset = numEntities - instList.m_GrassInstanceList.size();
|
|
|
|
//Display map data header:
|
|
spdAABB aabb = fwMapDataStore::GetStore().GetStreamingBounds(strLocalIndex(sMapDataDefIndex[sSelectedMapData]));
|
|
Displayf( "GRASS-DBG:\t%s (MapData Slot: %d): EntityBatch Count: %d\tGrassBatch Count: %d\tStreaming AABB[min|max] = {%f, %f, %f} | {%f, %f, %f}",
|
|
g_MapDataStore.GetName(strLocalIndex(sMapDataDefIndex[sSelectedMapData])), sMapDataDefIndex[sSelectedMapData], instList.m_PropInstanceList.size(), instList.m_GrassInstanceList.size(),
|
|
aabb.GetMin().GetXf(), aabb.GetMin().GetXf(), aabb.GetMin().GetXf(),
|
|
aabb.GetMax().GetXf(), aabb.GetMax().GetXf(), aabb.GetMax().GetXf() );
|
|
|
|
for(int i = instEntityOffset; i < grassEntityOffset; ++i)
|
|
{
|
|
CEntity *entity = static_cast<CEntity *>(contents->GetEntities()[i]);
|
|
if(entity && Verifyf(entity->GetIsTypeGrassInstanceList(), "Expected Grass Batch entity was incorrect type!"))
|
|
DisplayPropEntityInfo(static_cast<CEntityBatch *>(entity), "BATCH-DBG:\t\t", i - instEntityOffset);
|
|
}
|
|
|
|
for(int i = grassEntityOffset; i < numEntities; ++i)
|
|
{
|
|
CEntity *entity = static_cast<CEntity *>(contents->GetEntities()[i]);
|
|
if(entity && Verifyf(entity->GetIsTypeGrassInstanceList(), "Expected Grass Batch entity was incorrect type!"))
|
|
DisplayGrassEntityInfo(static_cast<CGrassBatch *>(entity), "GRASS-DBG:\t\t", i - grassEntityOffset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AddWidgets(bkBank &bank)
|
|
{
|
|
//Cache off main group ptr
|
|
sBank = &bank;
|
|
|
|
//Initialize MapDataDef Index Map
|
|
InitMapDataDefIndexMap();
|
|
|
|
//Selection Widgets
|
|
sSelectionGroup = bank.PushGroup("Select/Edit Batch", false);
|
|
{
|
|
bank.AddButton("Set Selected Entity From Picker", OnSetSelectedEntityFromPicker);
|
|
sMapNameComboWidget = AddMapNameComboWidget(bank);
|
|
}
|
|
bank.PopGroup();
|
|
|
|
//Performance & Usage Stats
|
|
bank.PushGroup("Performance & Usage Stats", false);
|
|
{
|
|
bank.AddSlider(" Shadow LOD Factor ", &sShadowLodFactor,0.f, 1.f, 0.001f);
|
|
bank.AddToggle("Enable Usage Info Display", &sPerfDisplayUsageInfo);
|
|
bank.AddSeparator("");
|
|
|
|
bank.AddToggle("Display Runtime Info", &sPerfDisplayRuntimeInfo);
|
|
#if GRASS_BATCH_CS_CULLING
|
|
bank.AddToggle("Display CS Culled Info - *WARNING! SLOW!*", &sPerfDisplayGrassCSInfo);
|
|
ORBIS_ONLY(bank.AddToggle("Add GPU Fence for CS Culled Info", &sPerfDisplayGrassCS_UseGPUFence));
|
|
#endif //GRASS_BATCH_CS_CULLING
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(bank.AddToggle("Display Pre-Allocated Buffer Info", &sPerfDisplayAllocatorInfo));
|
|
bank.AddToggle("Display Prop Usage Info", &sPerfDisplayPropUsageInfo);
|
|
bank.AddToggle("Display Grass Usage Info", &sPerfDisplayGrassUsageInfo);
|
|
}
|
|
bank.PopGroup();
|
|
|
|
//Debug Draw
|
|
bank.PushGroup("Debug Draw", false);
|
|
{
|
|
bank.AddToggle("Enable Debug Draw", &sDebugDrawEnabled);
|
|
bank.AddSeparator("");
|
|
|
|
bank.AddToggle("Draw Batch Bounds", &sDebugDrawBatchAABB);
|
|
bank.AddToggle("Draw Instance Bounds", &sDebugDrawInstances);
|
|
bank.AddToggle("Draw Linked Bounds", &sDebugDrawLinkedBounds);
|
|
|
|
bank.AddTitle("");
|
|
bank.AddToggle("Draw Linked Info - Rendered", &sDebugDrawLinkedRenderedInfo);
|
|
bank.AddToggle("Draw Linked Info - Not Rendered", &sDebugDrawLinkedNotRenderedInfo);
|
|
|
|
bank.PushGroup("Extra Debug Draw Params", false);
|
|
{
|
|
bank.AddToggle("Draw Grass Terrain Normal", &sDebugDrawGrassTerrainNormalEnabled);
|
|
bank.AddSlider("Terrain Normal Length", &sDebugDrawGrassTerrainNormalLength, 0.0f, 10.0f, 0.1f);
|
|
bank.AddSlider("Terrain Normal Cone Length", &sDebugDrawGrassTerrainNormalConeLength, 0.0f, 10.0f, 0.1f);
|
|
bank.AddSlider("Terrain Normal Cone Radius", &sDebugDrawGrassTerrainNormalConeRadius, 0.0f, 10.0f, 0.1f);
|
|
bank.AddColor("Terrain Normal Color", &sDebugDrawGrassTerrainNormalColor);
|
|
|
|
bank.AddTitle("");
|
|
bank.AddToggle("Draw Batch Bounds Solid", &sDebugDrawBatchAABBSolid);
|
|
bank.AddColor("Batch Bounds Color", &sDebugDrawBatchAABBColor);
|
|
|
|
bank.AddTitle("");
|
|
bank.AddToggle("Draw Instance Bounds Solid", &sDebugDrawInstanceAABBSolid);
|
|
bank.AddColor("Instance Bounds Color", &sDebugDrawInstanceAABBColor);
|
|
bank.AddColor("Linked Bounds - Drawn Color", &sDebugDrawRenderedHdAABBColor);
|
|
bank.AddColor("Linked Bounds - Not Drawn Color", &sDebugDrawNotRenderedHdAABBColor);
|
|
|
|
bank.AddTitle("");
|
|
bank.AddColor("Selected Bounds Color", &sDebugDrawSelectedBatchAABBColor);
|
|
bank.AddColor("Selected Batch Text Color", &sDebugDrawSelectedTextColor);
|
|
}
|
|
bank.PopGroup();
|
|
}
|
|
bank.PopGroup();
|
|
|
|
//CSE Widgets
|
|
CCustomShaderEffectGrass::AddWidgets(bank);
|
|
|
|
//Debug Widgets
|
|
bank.PushGroup("----== Debug ==----", false);
|
|
{
|
|
bank.PushGroup("Performance", false);
|
|
{
|
|
#if RSG_DURANGO || RSG_PC
|
|
#if RSG_PC
|
|
bank.AddToggle("Force Skip Instances", &bPerfForceSkipInstance);
|
|
#endif
|
|
bank.AddSlider("Skip Instances",&sPerfSkipInstance,0,15,1);
|
|
#endif // RSG_DURANGO || RSG_PC...
|
|
bank.AddToggle("GBuf: Only draw 1st instance of batch - Is draw call bound?", &sPerfOnlyDraw1stInstanceGBuf);
|
|
bank.AddToggle("Shadows: Only draw 1st instance of batch - Is draw call bound?", &sPerfOnlyDraw1stInstanceShadows);
|
|
#if GRASS_BATCH_CS_CULLING
|
|
bank.PushGroup("Compute Shader", false);
|
|
{
|
|
bank.AddToggle("Compute Shader: Enable", &sPerfEnableComputeShader);
|
|
bank.AddSeparator("");
|
|
bank.AddToggle("CS: Freeze Current Culling VP", &sGrassComputeShader_FreezeVp);
|
|
bank.AddToggle("CS: Allow Batching", &sGrassComputeShader_AllowBatching);
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(bank.AddToggle("CS: Allow CopyStructureCount Batching", &sGrassComputeShader_AllowCopyStructureCountBatching));
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(bank.AddToggle("CS: Avoid interleaving Dispatch() and CopySubresource() calls", &sGrassComputeShader_AvoidInterleaving));
|
|
bank.AddToggle("CS: Flush CS After Each Batch", &sFlushCSAfterEachDispatch);
|
|
bank.AddToggle("CS: LOD - Disable Crossfading", &sCSDisableCrossfade);
|
|
bank.AddToggle("CS: LOD - Disable LOD Scale", &sGrassComputeShader_IgnoreLodScale_DrawableLOD);
|
|
bank.AddToggle("CS: LOD Fade - Disable LOD Scale", &sGrassComputeShader_IgnoreLodScale_LODFade);
|
|
ORBIS_ONLY(bank.AddToggle("CS: Debug Draw Indirect Call", &sGrassComputeShader_DebugDrawIndirect));
|
|
bank.AddToggle("CS: UAV Sync" , &sGrassComputeShader_UAV_Sync);
|
|
bank.PushGroup("Frozen Viewport", false);
|
|
{
|
|
const Vec4V *planes = sFrozenVp.GetFrustumClipPlaneArray();
|
|
for(int i = 0; i < 6; ++i)
|
|
bank.AddVector("Viewport:", const_cast<Vec4V *>(&(planes[i])), -20000.0f, 20000.0f, 0.1f);
|
|
}
|
|
bank.PopGroup();
|
|
}
|
|
bank.PopGroup();
|
|
#endif //GRASS_BATCH_CS_CULLING
|
|
}
|
|
bank.PopGroup();
|
|
bank.AddToggle("Compute AO Per Instance", &sComputeAoScalePerInstance, UpdateAllBatchAoScales);
|
|
bank.AddToggle("Enable Dynamic Visibility From Entity Link", &sEnableLinkageVisibility);
|
|
bank.AddToggle("Allow Linkage Visibility Reset Each Frame", &sResetLinkageVisibility);
|
|
|
|
bank.AddToggle("Override Creation LOD Distance", &sOverrideLodDist);
|
|
bank.AddSlider("LOD Distance Value", &sOverriddenLodDistValue, 0u, 0x1fffu, 1u);
|
|
|
|
bank.AddSeparator("");
|
|
bank.AddToggle("Props: Output Static Instance Buffer", &sOutputStaticInstanceBuffer);
|
|
bank.AddToggle("Props: Output Static IB Data", &sOutputStaticIBData);
|
|
bank.AddToggle("Grass: Probe for actual terrain normal", &sProbeForNormal);
|
|
bank.AddButton("Display Selected Info", DisplaySelectedInfo);
|
|
|
|
bank.AddSeparator("");
|
|
bank.AddButton("Refresh MapData Map (dropdown)", RefreshMapDataDefIndexMap);
|
|
}
|
|
bank.PopGroup();
|
|
}
|
|
|
|
///////////////////////////////////
|
|
//Display Performance & Usage Stats
|
|
|
|
template <class T, class count_t>
|
|
count_t AccumulateArraySize(count_t count, const T &def)
|
|
{
|
|
return count + def.m_InstanceList.size();
|
|
}
|
|
|
|
size_t AccumulateIBMemUsage(size_t usage, const fwPropInstanceListDef &def)
|
|
{
|
|
return usage + (def.GetStaticInstanceBufferList() ? def.GetStaticInstanceBufferList()->GetIBMemUsage() : 0);
|
|
}
|
|
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(void DisplayAppendBufferAllocatorInfo(bool isProportional, int seperatorPixelHeight));
|
|
|
|
void DisplayUsageInfo()
|
|
{
|
|
static BankInt32 sSeperatorPixelHeight = 3;
|
|
static BankBool sProportional = true;
|
|
bool addSeperator = false;
|
|
|
|
if(sMapDataDefIndex.empty())
|
|
RefreshMapDataDefIndexMap(); //Make sure the data def map is valid
|
|
|
|
//Runtime stats:
|
|
#if GRASS_BATCH_CS_CULLING
|
|
if(sPerfDisplayRuntimeInfo && sPerfDisplayGrassCSInfo)
|
|
{
|
|
grcDebugDraw::AddDebugOutputEx(sProportional, " Pass # batches rendered # instances rendered # instances rendered (LOD Fade) # Inst CS # 0-Size Batches Culled");
|
|
grcDebugDraw::AddDebugOutputEx(sProportional, "GBuff: %9d %9d %9d %9d %9d", sPerfBatchesRenderedGbuf, sPerfInstancesRenderedGbuf, sPerfInstancesRenderedGbuf_LODFade, sPerfCSInstancesRenderedGbuf, sPerfCSZeroCountBatchesGbuf);
|
|
grcDebugDraw::AddDebugOutputEx(sProportional, "Shadow: %9d %9d %9d %9d %9d", sPerfBatchesRenderedShadow, sPerfInstancesRenderedShadow, sPerfInstancesRenderedShadow_LODFade, sPerfCSInstancesRenderedShadow, sPerfCSZeroCountBatchesShadow);
|
|
if(sPerfBatchesRenderedOther > 0)
|
|
grcDebugDraw::AddDebugOutputEx(sProportional, "Other: %9d %9d %9d %9d %9d", sPerfBatchesRenderedOther, sPerfInstancesRenderedOther, sPerfInstancesRenderedOther_LODFade, sPerfCSInstancesRenderedOther, sPerfCSZeroCountBatchesOther);
|
|
grcDebugDraw::AddDebugOutputSeparator(sSeperatorPixelHeight);
|
|
}
|
|
else
|
|
#endif //GRASS_BATCH_CS_CULLING
|
|
if(sPerfDisplayRuntimeInfo)
|
|
{
|
|
grcDebugDraw::AddDebugOutputEx(sProportional, " Pass # batches rendered # instances rendered # instances rendered (LOD Fade)");
|
|
grcDebugDraw::AddDebugOutputEx(sProportional, "GBuff: %9d %9d %9d", sPerfBatchesRenderedGbuf, sPerfInstancesRenderedGbuf, sPerfInstancesRenderedGbuf_LODFade);
|
|
grcDebugDraw::AddDebugOutputEx(sProportional, "Shadow: %9d %9d %9d", sPerfBatchesRenderedShadow, sPerfInstancesRenderedShadow, sPerfInstancesRenderedShadow_LODFade);
|
|
if(sPerfBatchesRenderedOther > 0)
|
|
grcDebugDraw::AddDebugOutputEx(sProportional, "Other: %9d %9d %9d", sPerfBatchesRenderedOther, sPerfInstancesRenderedOther, sPerfInstancesRenderedOther_LODFade);
|
|
grcDebugDraw::AddDebugOutputSeparator(sSeperatorPixelHeight);
|
|
}
|
|
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(if(sPerfDisplayAllocatorInfo) DisplayAppendBufferAllocatorInfo(sProportional, sSeperatorPixelHeight));
|
|
|
|
grcDebugDraw::AddDebugOutputEx(sProportional, " Map Name # batches # instances MapData Mem (KB) IB Mem (KB)");
|
|
|
|
rage::atArray<s32>::const_iterator end = sMapDataDefIndex.end();
|
|
rage::atArray<s32>::const_iterator iter;
|
|
if(sPerfDisplayPropUsageInfo)
|
|
{
|
|
u32 totalMaps = 0; u32 totalBatches = 0; u32 totalInstances = 0; size_t totalMapMem = 0; size_t totalIBMem = 0;
|
|
for(iter = sMapDataDefIndex.begin(); iter != end; ++iter)
|
|
{
|
|
fwMapDataDef *pDef = g_MapDataStore.GetSlot(strLocalIndex(*iter));
|
|
if(pDef && pDef->IsLoaded() && pDef->GetContents() && pDef->GetContents()->GetLoaderInstRef().GetInstance())
|
|
{
|
|
fwMapDataContents *contents = pDef->GetContents();
|
|
fwInstancedMapData &instList = reinterpret_cast<fwMapData *>(contents->GetLoaderInstRef().GetInstance())->m_instancedData;
|
|
if(!instList.m_PropInstanceList.empty())
|
|
{
|
|
typedef fwPropInstanceListDef::InstanceDataList InstanceDataList;
|
|
const size_t defSize = sizeof(fwPropInstanceListDef);
|
|
const size_t instSize = sizeof(InstanceDataList::value_type);
|
|
int numBatches = instList.m_PropInstanceList.size();
|
|
InstanceDataList::size_type numInstances = 0;
|
|
numInstances = std::accumulate(instList.m_PropInstanceList.begin(), instList.m_PropInstanceList.end(), numInstances, std::ptr_fun(&AccumulateArraySize<fwPropInstanceListDef, InstanceDataList::size_type>));
|
|
size_t mapMem = defSize * numBatches + instSize * numInstances;
|
|
|
|
size_t ibMem = 0;
|
|
ibMem = std::accumulate(instList.m_PropInstanceList.begin(), instList.m_PropInstanceList.end(), ibMem, std::ptr_fun(&AccumulateIBMemUsage));
|
|
|
|
grcDebugDraw::AddDebugOutputEx( sProportional, "%-32s %11d %11d %16d %11d", g_MapDataStore.GetName(strLocalIndex(*iter)), numBatches, numInstances, mapMem / 1024, ibMem / 1024);
|
|
|
|
//Store totals:
|
|
++totalMaps; totalBatches += numBatches; totalInstances += numInstances; totalMapMem += mapMem; totalIBMem += ibMem;
|
|
addSeperator = true;
|
|
}
|
|
}
|
|
}
|
|
if(addSeperator)
|
|
grcDebugDraw::AddDebugOutputEx( sProportional, "Totals: %11d Maps %11d %11d %16d %11d", totalMaps, totalBatches, totalInstances, totalMapMem / 1024, totalIBMem / 1024);
|
|
}
|
|
|
|
if(addSeperator)
|
|
grcDebugDraw::AddDebugOutputSeparator(sSeperatorPixelHeight);
|
|
|
|
addSeperator = false;
|
|
if(sPerfDisplayGrassUsageInfo)
|
|
{
|
|
u32 totalMaps = 0; u32 totalBatches = 0; u32 totalInstances = 0; size_t totalMapMem = 0;
|
|
for(iter = sMapDataDefIndex.begin(); iter != end; ++iter)
|
|
{
|
|
fwMapDataDef *pDef = g_MapDataStore.GetSlot(strLocalIndex(*iter));
|
|
if(pDef && pDef->IsLoaded() && pDef->GetContents() && pDef->GetContents()->GetLoaderInstRef().GetInstance())
|
|
{
|
|
fwMapDataContents *contents = pDef->GetContents();
|
|
fwInstancedMapData &instList = reinterpret_cast<fwMapData *>(contents->GetLoaderInstRef().GetInstance())->m_instancedData;
|
|
if(!instList.m_GrassInstanceList.empty())
|
|
{
|
|
typedef fwGrassInstanceListDef::InstanceDataList InstanceDataList;
|
|
const size_t defSize = sizeof(fwGrassInstanceListDef);
|
|
const size_t instSize = sizeof(InstanceDataList::value_type);
|
|
int numBatches = instList.m_GrassInstanceList.size();
|
|
InstanceDataList::size_type numInstances = 0;
|
|
numInstances = std::accumulate(instList.m_GrassInstanceList.begin(), instList.m_GrassInstanceList.end(), numInstances, std::ptr_fun(&AccumulateArraySize<fwGrassInstanceListDef, InstanceDataList::size_type>));
|
|
size_t mem = defSize * numBatches + instSize * numInstances;
|
|
|
|
grcDebugDraw::AddDebugOutputEx( sProportional, "%32s %11d %11d %16d %11s", g_MapDataStore.GetName(strLocalIndex(*iter)), numBatches, numInstances, mem / 1024, "<N/A>");
|
|
|
|
//Store totals:
|
|
++totalMaps; totalBatches += numBatches; totalInstances += numInstances; totalMapMem += mem;
|
|
addSeperator = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(addSeperator)
|
|
grcDebugDraw::AddDebugOutputEx( sProportional, "Totals: %11d Maps %11d %11d %16d %11s", totalMaps, totalBatches, totalInstances, totalMapMem / 1024, "<N/A>");
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////
|
|
// Debug Draw functionality
|
|
|
|
inline void CopyMatrixFromInstance(const CEntityBatch *UNUSED_PARAM(eb), const CEntityBatch::InstanceList::InstanceData &inst, Mat34V_InOut mat)
|
|
{
|
|
inst.GetMatrixCopy(mat);
|
|
}
|
|
|
|
inline void CopyMatrixFromInstance(const CGrassBatch *eb, const CGrassBatch::InstanceList::InstanceData &inst, Mat34V_InOut mat)
|
|
{
|
|
Vec3V position(V_ZERO);
|
|
if(eb && eb->GetInstanceList())
|
|
{
|
|
//numeric_limits w/ decltype don't work, presumably b/c of STL port.
|
|
//CompileTimeAssert(std::is_arithmetic<decltype(iter->m_Position[0])>()); //If this isn't the case, the line below will likely fail.
|
|
//const ScalarV dist = ScalarV(static_cast<float>(std::numeric_limits<decltype(iter->m_Position[0])>::max()));
|
|
//const ScalarV dist = ScalarV(static_cast<float>(decltype(iter->m_Position[0])(-1)));
|
|
const ScalarV dist = ScalarV(static_cast<float>(u16(-1)));
|
|
|
|
const Vec3V offset = Saturate(Vec3V(static_cast<float>(inst.m_Position[0]), static_cast<float>(inst.m_Position[1]), static_cast<float>(inst.m_Position[2])) / dist);
|
|
const Vec3V basePos = eb->GetInstanceList()->m_BatchAABB.GetMin();
|
|
const Vec3V offsetScale = eb->GetInstanceList()->m_BatchAABB.GetMax() - basePos;
|
|
position = basePos + (offset * offsetScale);
|
|
}
|
|
|
|
mat.SetIdentity3x3();
|
|
mat.Setd(position);
|
|
}
|
|
|
|
void DebugDrawEntityBatchInstance_Additional(const CEntityBatch *UNUSED_PARAM(eb), const CEntityBatch::InstanceList::InstanceData &UNUSED_PARAM(inst), Mat34V_InOut UNUSED_PARAM(mat))
|
|
{
|
|
//Do nothing for prop batches
|
|
}
|
|
|
|
void DebugDrawEntityBatchInstance_Additional(const CGrassBatch *UNUSED_PARAM(eb), const CGrassBatch::InstanceList::InstanceData &inst, Mat34V_InOut mat)
|
|
{
|
|
//Draw terrain normal
|
|
if(sDebugDrawGrassTerrainNormalEnabled)
|
|
{
|
|
Vec3V normal = inst.ComputeNormal();
|
|
Vec3V v1 = mat.d();
|
|
Vec3V v2 = v1 + (normal * ScalarV(sDebugDrawGrassTerrainNormalLength));
|
|
grcDebugDraw::Line(v1, v2, sDebugDrawGrassTerrainNormalColor);
|
|
|
|
ScalarV coneLen(sDebugDrawGrassTerrainNormalConeLength / 2.0f);
|
|
Vec3V c1 = v2 + (normal * coneLen);
|
|
Vec3V c2 = v2 - (normal * coneLen);
|
|
grcDebugDraw::Cone(c1, c2, sDebugDrawGrassTerrainNormalConeRadius, sDebugDrawGrassTerrainNormalColor, sDebugDrawGrassTerrainNormalConeCap);
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
void DebugDrawEntityBatchInstance(const T *eb, const typename T::InstanceList::InstanceData &inst)
|
|
{
|
|
CBaseModelInfo *pModelInfo = (eb ? eb->GetBaseModelInfo() : NULL);
|
|
if(pModelInfo)
|
|
{
|
|
Mat34V mat;
|
|
spdAABB aabb = pModelInfo->GetBoundingBox();
|
|
CopyMatrixFromInstance(eb, inst, mat);
|
|
grcDebugDraw::BoxOriented(aabb.GetMin(), aabb.GetMax(), mat, sDebugDrawInstanceAABBColor, sDebugDrawInstanceAABBSolid);
|
|
|
|
//Draw additional per-instance stuff?
|
|
DebugDrawEntityBatchInstance_Additional(eb, inst, mat);
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
void DebugDrawLinkedEntityBounds(const T &inst, fwMapDataDef *pDef)
|
|
{
|
|
if(pDef && pDef->IsLoaded() && pDef->GetContents())
|
|
{
|
|
fwMapDataContents *contents = pDef->GetContents();
|
|
fwEntity** entities = contents->GetEntities();
|
|
u32 numEntities = contents->GetNumEntities();
|
|
|
|
if(inst.m_Index < numEntities)
|
|
{
|
|
if(CEntity *entity = static_cast<CEntity *>(entities[inst.m_Index]))
|
|
{
|
|
if(CBaseModelInfo *pModelInfo = entity->GetBaseModelInfo())
|
|
{
|
|
spdAABB aabb = pModelInfo->GetBoundingBox();
|
|
bool drawnThisFrame = entity->GetAddedToDrawListThisFrame(DL_RENDERPHASE_GBUF);
|
|
grcDebugDraw::BoxOriented(aabb.GetMin(), aabb.GetMax(), entity->GetNonOrthoMatrix(), drawnThisFrame ? sDebugDrawRenderedHdAABBColor : sDebugDrawNotRenderedHdAABBColor, sDebugDrawInstanceAABBSolid);
|
|
|
|
if((sDebugDrawLinkedRenderedInfo && drawnThisFrame) || (sDebugDrawLinkedNotRenderedInfo && !drawnThisFrame))
|
|
{
|
|
char buf[256];
|
|
entity->GetAABB(aabb);
|
|
float distFromCamera = Dist(VECTOR3_TO_VEC3V(camInterface::GetPos()), aabb.GetCenter()).Getf(); //Show dist from camera to make setting lodDist easier.
|
|
const char *modelName = "";
|
|
#if !__NO_OUTPUT
|
|
modelName = entity->GetModelName();
|
|
#endif //!__NO_OUTPUT
|
|
formatf(buf, sizeof(buf) / sizeof(char), "%s : %d [Rendered = %s - LodDist = %d - DistFromCamera = %f]", modelName,
|
|
inst.m_Index, drawnThisFrame ? "true" : "false", entity->GetLodDistance(), distFromCamera);
|
|
grcDebugDraw::Text(aabb.GetCenter(), sDebugDrawSelectedTextColor, buf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
bool IsSelected(T *eb)
|
|
{
|
|
bool selected = false;
|
|
|
|
if(eb && sSelectedMapData >= 0)
|
|
{
|
|
if(eb->GetMapDataDefIndex() == sMapDataDefIndex[sSelectedMapData])
|
|
{
|
|
if(sSelectedEntityBatch >= 0)
|
|
{
|
|
selected = (static_cast<CEntity *>(GetSelectedEntity()) == static_cast<CEntity *>(eb));
|
|
}
|
|
else
|
|
selected = true;
|
|
}
|
|
}
|
|
|
|
return selected;
|
|
}
|
|
|
|
//Grass batches are not linked, so they should not call this function.
|
|
void DebugDrawLinkedEntities(CEntityBatch *eb, bool selected)
|
|
{
|
|
if(eb)
|
|
{
|
|
if(const CEntityBatch::InstanceList *list = eb->GetInstanceList())
|
|
{
|
|
if(sDebugDrawLinkedBounds && (sDebugDrawEnabled || (selected && sSelectedEntityBatchInst < 0)))
|
|
{
|
|
fwMapDataDef *pDef = (eb->GetHdMapDataDefIndex() > -1 ? g_MapDataStore.GetSlot(strLocalIndex(eb->GetHdMapDataDefIndex())) : NULL);
|
|
//since we have removed stlport stl and atArray don't seem to want to play well together
|
|
//std::for_each(list->m_InstanceList.begin(), list->m_InstanceList.end(), std::bind2nd(std::ptr_fun(&DebugDrawLinkedEntityBounds<CEntityBatch::InstanceList::InstanceData>), pDef));
|
|
for (rage::fwPropInstanceListDef::InstanceDataList::const_iterator i = list->m_InstanceList.begin(); i != list->m_InstanceList.end();i++)
|
|
{
|
|
DebugDrawLinkedEntityBounds((*i), pDef);
|
|
}
|
|
}
|
|
|
|
if(selected && sSelectedEntityBatchInst > -1) //Draw selected inst?
|
|
{
|
|
fwMapDataDef *pDef = (eb->GetHdMapDataDefIndex() > -1 ? g_MapDataStore.GetSlot(strLocalIndex(eb->GetHdMapDataDefIndex())) : NULL);
|
|
DebugDrawLinkedEntityBounds(list->m_InstanceList[sSelectedEntityBatchInst], pDef);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
void DebugDrawBatch(const T *eb, bool selected, int index)
|
|
{
|
|
if(eb)
|
|
{
|
|
typedef typename T::InstanceList InstanceList;
|
|
typedef typename T::InstanceList::InstanceData InstanceData;
|
|
typedef typename T::InstanceList::InstanceDataList::const_iterator InstanceDataIterator;
|
|
const InstanceList *list = eb->GetInstanceList();
|
|
if(list)
|
|
{
|
|
if(sDebugDrawEnabled && sDebugDrawBatchAABB && !selected)
|
|
grcDebugDraw::BoxAxisAligned(list->m_BatchAABB.GetMin(), list->m_BatchAABB.GetMax(), sDebugDrawBatchAABBColor, sDebugDrawBatchAABBSolid);
|
|
|
|
if(sDebugDrawInstances && (sDebugDrawEnabled || (selected && sSelectedEntityBatchInst < 0)))
|
|
{
|
|
//since we have removed stlport stl and atArray don't seem to want to play well together
|
|
//std::for_each(list->m_InstanceList.begin(), list->m_InstanceList.end(), std::bind1st(std::ptr_fun(&DebugDrawEntityBatchInstance<T>), eb));
|
|
for (InstanceDataIterator i = list->m_InstanceList.begin(); i != list->m_InstanceList.end();i++)
|
|
{
|
|
DebugDrawEntityBatchInstance(eb,(*i));
|
|
}
|
|
}
|
|
}
|
|
|
|
//Show model aabb transformed by the computed re-scale matrix to emulate the batch aabb.
|
|
if(selected || (sDebugDrawEnabled && sDebugDrawEntityTransformAABB))
|
|
{
|
|
spdAABB aabb;
|
|
eb->GetAABB(aabb);
|
|
grcDebugDraw::BoxAxisAligned(aabb.GetMin(), aabb.GetMax(), selected ? sDebugDrawSelectedBatchAABBColor : sDebugDrawEntityTransformAABBColor, selected ? false : sDebugDrawEntityTransformAABBSolid);
|
|
|
|
if(selected)
|
|
{
|
|
static const char *sUnknownArchetype = "<Unknown Archetype Name>";
|
|
const char *archetypeName = (list ? list->m_archetypeName.GetCStr() : sUnknownArchetype);
|
|
float distFromCamera = Dist(VECTOR3_TO_VEC3V(camInterface::GetPos()), aabb.GetCenter()).Getf(); //Show dist from camera to make setting lodDist easier.
|
|
|
|
char buf[256];
|
|
formatf(buf, sizeof(buf) / sizeof(char), "[%d]%s - %s [DistFromCamera = %f]", index, archetypeName, g_MapDataStore.GetName(strLocalIndex(sMapDataDefIndex[sSelectedMapData])), distFromCamera);
|
|
grcDebugDraw::Text(aabb.GetCenter(), sDebugDrawSelectedTextColor, buf);
|
|
|
|
//Draw selected inst?
|
|
if(sSelectedEntityBatchInst > -1 && list)
|
|
DebugDrawEntityBatchInstance(eb, list->m_InstanceList[sSelectedEntityBatchInst]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int DebugGetEBIndex(CEntity *eb)
|
|
{
|
|
if(sSelectedMapData == -1)
|
|
return -1;
|
|
|
|
return FindEBIndexInMap(eb, sMapDataDefIndex[sSelectedMapData]);
|
|
}
|
|
|
|
bool DebugDrawEntityBatch(void *pItem, void *UNUSED_PARAM(data))
|
|
{
|
|
if(CEntity *entity = static_cast<CEntity *>(pItem))
|
|
{
|
|
if(entity->GetIsTypeInstanceList())
|
|
{
|
|
CEntityBatch *eb = static_cast<CEntityBatch *>(entity);
|
|
bool selected = IsSelected(eb);
|
|
int index = selected ? DebugGetEBIndex(eb) : -1;
|
|
DebugDrawBatch(eb, selected, index);
|
|
DebugDrawLinkedEntities(eb, selected);
|
|
}
|
|
else if(entity->GetIsTypeGrassInstanceList())
|
|
{
|
|
CGrassBatch *eb = static_cast<CGrassBatch *>(entity);
|
|
bool selected = IsSelected(eb);
|
|
int index = selected ? DebugGetEBIndex(eb) : -1;
|
|
DebugDrawBatch(eb, selected, index);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsDebugDrawEnabled()
|
|
{
|
|
return (sDebugDrawEnabled && (sDebugDrawBatchAABB || sDebugDrawInstances || sDebugDrawEntityTransformAABB)) || sSelectedMapData >= 0;
|
|
}
|
|
|
|
#if GRASS_BATCH_CS_CULLING
|
|
void DrawCSFrozenFrustum()
|
|
{
|
|
if(sGrassComputeShader_FreezeVp)
|
|
{
|
|
sFrustumDbg.Set(sFrozenVp);
|
|
sFrustumDbg.DebugRender();
|
|
}
|
|
}
|
|
#endif //GRASS_BATCH_CS_CULLING
|
|
|
|
void RenderDebug()
|
|
{
|
|
if(IsDebugDrawEnabled())
|
|
{
|
|
CEntityBatch::GetPool()->ForAll(EBStatic::DebugDrawEntityBatch, NULL);
|
|
CGrassBatch::GetPool()->ForAll(EBStatic::DebugDrawEntityBatch, NULL);
|
|
}
|
|
|
|
//Draw selected streaming bounds if it's not loaded to make it easier to find.
|
|
if(sSelectedMapData >= 0)
|
|
{
|
|
fwMapDataDef *pDef = g_MapDataStore.GetSlot(strLocalIndex(sMapDataDefIndex[sSelectedMapData]));
|
|
if(pDef)
|
|
{
|
|
const spdAABB &aabb = fwMapDataStore::GetStore().GetStreamingBounds(strLocalIndex(sMapDataDefIndex[sSelectedMapData]));
|
|
grcDebugDraw::BoxAxisAligned(aabb.GetMin(), aabb.GetMax(), pDef->IsLoaded() ? sDebugDrawMapAABBLoadedColor : sDebugDrawMapAABBUnloadedColor, false);
|
|
//grcDebugDraw::Text(aabb.GetCenter(), sDebugDrawSelectedTextColor, g_MapDataStore.GetName(sMapDataDefIndex[sSelectedMapData]));
|
|
}
|
|
}
|
|
|
|
//Performance & Usage Stats
|
|
if(sPerfDisplayUsageInfo)
|
|
DisplayUsageInfo();
|
|
|
|
GRASS_BATCH_CS_CULLING_ONLY(DrawCSFrozenFrustum());
|
|
}
|
|
|
|
void Init(unsigned initMode)
|
|
{
|
|
static bool sMapDataDefIndexMapNeedsInit = true; //Only need to do this once.
|
|
if(sMapDataDefIndexMapNeedsInit && initMode == INIT_SESSION)
|
|
{
|
|
if(sMapNameComboWidget) //If bank hasn't been created yet, map data def index map will get update when needed.
|
|
EBStatic::RefreshMapDataDefIndexMap();
|
|
|
|
sMapDataDefIndexMapNeedsInit = false;
|
|
}
|
|
|
|
#if GRASS_BATCH_CS_CULLING
|
|
if (PARAM_disableGrassComputeShader.Get())
|
|
{
|
|
sPerfEnableComputeShader = false;
|
|
}
|
|
if (PARAM_grassDisableRenderGeometry.Get())
|
|
{
|
|
sGrassRenderGeometry = false;
|
|
}
|
|
#endif // GRASS_BATCH_CS_CULLING
|
|
}
|
|
#endif //__BANK
|
|
|
|
bool ResetHdEntityVisibilityForBatch(void *pItem, void *UNUSED_PARAM(data))
|
|
{
|
|
if(CEntityBatch *eb = static_cast<CEntityBatch *>(pItem))
|
|
{
|
|
const fwPropInstanceListDef *list = eb->GetInstanceList();
|
|
fwMapDataDef *pDef = (eb->GetHdMapDataDefIndex() > -1 ? g_MapDataStore.GetSlot(strLocalIndex(eb->GetHdMapDataDefIndex())) : NULL);
|
|
if(list && pDef && pDef->IsLoaded() && pDef->GetContents())
|
|
{
|
|
fwMapDataContents *contents = pDef->GetContents();
|
|
fwEntity** entities = contents->GetEntities();
|
|
u32 numEntities = contents->GetNumEntities();
|
|
|
|
fwPropInstanceListDef::InstanceDataList::const_iterator end = list->m_InstanceList.end();
|
|
fwPropInstanceListDef::InstanceDataList::const_iterator iter;
|
|
for(iter = list->m_InstanceList.begin(); iter != end; ++iter)
|
|
{
|
|
if(iter->m_Index < numEntities)
|
|
{
|
|
if(CEntity *entity = static_cast<CEntity *>(entities[iter->m_Index]))
|
|
entity->SetAddedToDrawListThisFrame(DL_RENDERPHASE_GBUF, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ResetHdEntityVisibility()
|
|
{
|
|
if(EBStatic::sResetLinkageVisibility)
|
|
CEntityBatch::GetPool()->ForAll(EBStatic::ResetHdEntityVisibilityForBatch, NULL);
|
|
}
|
|
|
|
//Compute Shader
|
|
#if GRASS_BATCH_CS_CULLING
|
|
inline const grcViewport *GetClippingVP()
|
|
{
|
|
const grcViewport *vp = dlCmdSetCurrentViewport::GetCurrentViewport_UpdateThread();
|
|
#if __BANK
|
|
if(DRAWLISTMGR->IsBuildingGBufDrawList())
|
|
{
|
|
if(sGrassComputeShader_FreezeVp)
|
|
{
|
|
vp = &sFrozenVp;
|
|
}
|
|
else if(vp)
|
|
{
|
|
sFrozenVp = *vp;
|
|
}
|
|
}
|
|
#endif
|
|
return vp;
|
|
}
|
|
#endif //GRASS_BATCH_CS_CULLING
|
|
|
|
#if GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
//Pre-allocated pool of grass append buffers.
|
|
template<class T, u32 _InitialPreAllocatedCount = static_cast<u32>(1000 * grcInstanceBuffer::MaxFrames)>
|
|
class PreAllocatedResourcePool
|
|
{
|
|
public:
|
|
static const u32 sDefaultInitialPreAllocatedCount = _InitialPreAllocatedCount;
|
|
u32 actualInitialPreAllocatedCount;
|
|
typedef T value_type;
|
|
typedef stlList<T> list_type;
|
|
typedef typename list_type::size_type size_type;
|
|
|
|
PreAllocatedResourcePool() : m_CurrFrame(fwTimer::GetSystemFrameCount()),actualInitialPreAllocatedCount(0) { }
|
|
|
|
void PreAllocateResources(u32 count = sDefaultInitialPreAllocatedCount)
|
|
{
|
|
const T initVal; //Let's just create/destroy 1 for the whole pre-allocate process.
|
|
for(u32 i = 0; i < count; ++i)
|
|
{
|
|
m_FreeList.push_back(initVal);
|
|
m_FreeList.back().PreCreate();
|
|
}
|
|
actualInitialPreAllocatedCount = count;
|
|
}
|
|
|
|
T *Allocate()
|
|
{
|
|
if(m_CurrFrame != fwTimer::GetSystemFrameCount())
|
|
NextFrame();
|
|
|
|
//Check if there are enough elements in the free list, and allocate overflow if there is not.
|
|
if(m_FreeList.empty())
|
|
{
|
|
renderWarningf("%s: Pre-Allocated Resource List Is Empty! Allocating overflow resources for now, which can cause stalls. Please report this assert to graphics so we can bump the pre-allocated count. [Pre-Allocated Count = %d | Current Count = %d]", typeid(T).name(), actualInitialPreAllocatedCount, GetAllocatedCount());
|
|
m_FreeList.push_back(T());
|
|
m_FreeList.back().Create(ASSERT_ONLY(false));
|
|
}
|
|
|
|
//Splice 1st free resource into current alloc list and return it.
|
|
u32 currList = m_CurrFrame % static_cast<u32>(m_AllocList.GetMaxCount());
|
|
m_AllocList[currList].splice(m_AllocList[currList].end(), m_FreeList, m_FreeList.begin());
|
|
return &(m_AllocList[currList].back());
|
|
}
|
|
|
|
size_type GetAllocatedCount() const
|
|
{
|
|
return std::accumulate(m_AllocList.begin(), m_AllocList.end(), size_type(0), [](size_type count, const list_type &list){ return count + list.size(); });
|
|
}
|
|
|
|
size_type GetFreeCount() const
|
|
{
|
|
return m_FreeList.size();
|
|
}
|
|
|
|
private:
|
|
list_type m_FreeList;
|
|
atRangeArray<list_type, grcInstanceBuffer::MaxFrames> m_AllocList;
|
|
u32 m_CurrFrame;
|
|
|
|
void NextFrame()
|
|
{
|
|
u32 nextFrame = fwTimer::GetSystemFrameCount();
|
|
u32 maxFrames = static_cast<u32>(m_AllocList.GetMaxCount());
|
|
u32 framesElapsed = Min(nextFrame - m_CurrFrame, maxFrames);
|
|
|
|
//Reap allocated resources.
|
|
for(int i = 0; i < framesElapsed; ++i)
|
|
m_FreeList.splice(m_FreeList.end(), m_AllocList[(m_CurrFrame + 1 + i) % maxFrames]);
|
|
|
|
m_CurrFrame = nextFrame;
|
|
}
|
|
};
|
|
|
|
typedef PreAllocatedResourcePool<PreAllocatedAppendBuffer> PreAllocatedAppendBufferPool;
|
|
PreAllocatedAppendBufferPool sAppendBufferPool;
|
|
|
|
typedef PreAllocatedResourcePool<PreAllocatedIndirectArgBuffer> PreAllocatedIndirectArgBufferPool;
|
|
PreAllocatedIndirectArgBufferPool sIndirectArgBufferPool;
|
|
|
|
#if __BANK
|
|
void DisplayAppendBufferAllocatorInfo(bool isProportional, int seperatorPixelHeight)
|
|
{
|
|
grcDebugDraw::AddDebugOutputEx(isProportional, "Append Buffer List:\tFree Count\tAllocated Count");
|
|
grcDebugDraw::AddDebugOutputEx(isProportional, " \t %9d %9d", sAppendBufferPool.GetFreeCount(), sAppendBufferPool.GetAllocatedCount());
|
|
grcDebugDraw::AddDebugOutputSeparator(seperatorPixelHeight);
|
|
}
|
|
#endif //__BANK
|
|
#endif //GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Entity Batch Base Implementation
|
|
//
|
|
|
|
template <class S, class T>
|
|
CEntityBatchBase<S, T>::CEntityBatchBase(const eEntityOwnedBy ownedBy)
|
|
: parent_type(ownedBy)
|
|
, m_InstanceList(NULL)
|
|
, m_MapDataDefIndex(-1)
|
|
{
|
|
SetVisibilityType( VIS_ENTITY_INSTANCELIST );
|
|
}
|
|
|
|
template <class S, class T>
|
|
void CEntityBatchBase<S, T>::InitVisibilityFromDefinition(const typename CEntityBatchBase<S, T>::InstanceList *ASSERT_ONLY(definition), fwArchetype *archetype)
|
|
{
|
|
if(archetype == NULL)
|
|
return;
|
|
|
|
CBaseModelInfo &modelInfo = *static_cast<CBaseModelInfo *>(archetype);
|
|
fwFlags16 &visibilityMask = GetRenderPhaseVisibilityMask();
|
|
|
|
// set archetype flags
|
|
if(modelInfo.GetDontCastShadows())
|
|
visibilityMask.ClearFlag(VIS_PHASE_MASK_SHADOWS);
|
|
|
|
if(modelInfo.GetIsShadowProxy())
|
|
visibilityMask.SetFlag(VIS_PHASE_MASK_SHADOW_PROXY);
|
|
|
|
if(modelInfo.GetHasDrawableProxyForWaterReflections())
|
|
{
|
|
visibilityMask.SetFlag(VIS_PHASE_MASK_WATER_REFLECTION_PROXY);
|
|
|
|
#if __ASSERT
|
|
if(!visibilityMask.IsFlagSet(VIS_PHASE_MASK_WATER_REFLECTION))
|
|
Assertf(0, "%s is a water reflection proxy but does not render in water reflection", definition->m_archetypeName.GetCStr());
|
|
#endif // __ASSERT
|
|
}
|
|
|
|
#if WATER_REFLECTION_PRE_REFLECTED
|
|
if(modelInfo.GetHasPreReflectedWaterProxy())
|
|
{
|
|
visibilityMask.SetFlag(VIS_PHASE_MASK_WATER_REFLECTION_PROXY_PRE_REFLECTED);
|
|
}
|
|
|
|
#if __ASSERT
|
|
if(visibilityMask.IsFlagSet(VIS_PHASE_MASK_WATER_REFLECTION_PROXY_PRE_REFLECTED))
|
|
{
|
|
if(!visibilityMask.IsFlagSet(VIS_PHASE_MASK_WATER_REFLECTION))
|
|
Assertf(0, "%s has a pre-reflected water proxy but is not visible in water reflection", definition->m_archetypeName.GetCStr());
|
|
if(!visibilityMask.IsFlagSet(VIS_PHASE_MASK_WATER_REFLECTION_PROXY))
|
|
Warningf("%s has a pre-reflected water proxy but is not actually a water reflection proxy", definition->m_archetypeName.GetCStr());
|
|
}
|
|
#endif // __ASSERT
|
|
#endif // WATER_REFLECTION_PRE_REFLECTED
|
|
}
|
|
|
|
template <class S, class T>
|
|
void CEntityBatchBase<S, T>::InitBatchTransformFromDefinition(const typename CEntityBatchBase<S, T>::InstanceList *UNUSED_PARAM(definition), fwArchetype *archetype)
|
|
{
|
|
//Note: In order to keep from making parent_type::GetAABB virtual, we are using the fact that it transforms AABB by the entity matrix. So here we need to compute
|
|
//a transform that transforms from the model AABB to the batch AABB.
|
|
spdAABB modelAABB = archetype->GetBoundingBox();
|
|
Vec3V modelCenter = modelAABB.GetCenter();
|
|
Vec3V modelExtent = modelAABB.GetExtent();
|
|
|
|
Vec3V batchCenter = m_InstanceList->m_BatchAABB.GetCenter();
|
|
Vec3V batchExtent = m_InstanceList->m_BatchAABB.GetExtent();
|
|
|
|
Vec3V scale = InvScale(batchExtent, modelExtent); //Shouldn't need InvScaleSafe since the model/batch AABB should never be 0/an invalid value.
|
|
|
|
//Transform steps:
|
|
// 1 - transform modelCenter to origin
|
|
// 2 - Scale to batchExtent / modelExtent (scale to 1, then to batch extent)
|
|
// 3 - transform position to batchCenter
|
|
// Can condense 2 & 3
|
|
|
|
Mat44V modelToOriginMat, scalePlusOriginToBatchMat, compositeMtx;
|
|
Mat44VFromTranslation(modelToOriginMat, Vec4V(-modelCenter, ScalarV(V_ONE)));
|
|
Mat44VFromScale(scalePlusOriginToBatchMat, scale, Vec4V(batchCenter, ScalarV(V_ONE)));
|
|
Multiply(compositeMtx, scalePlusOriginToBatchMat, modelToOriginMat);
|
|
|
|
//Create transform
|
|
#if ENABLE_MATRIX_MEMBER
|
|
Mat34V m = compositeMtx.GetMat34();
|
|
SetTransform(m);
|
|
SetTransformScale(1.0f, 1.0f);
|
|
#else
|
|
fwMatrixTransform *trans = rage_new fwMatrixTransform(compositeMtx.GetMat34());
|
|
SetTransform(trans);
|
|
#endif
|
|
}
|
|
|
|
template <class S, class T>
|
|
void CEntityBatchBase<S, T>::SetModelId(fwModelId modelId)
|
|
{
|
|
parent_type::SetModelId(modelId);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Entity Batch Implementation
|
|
//
|
|
|
|
CEntityBatch::CEntityBatch(const eEntityOwnedBy ownedBy)
|
|
: parent_type(ownedBy)
|
|
, m_Lod(0)
|
|
, m_HdMapDataDefIndex(-1)
|
|
{
|
|
SetTypeInstanceList();
|
|
}
|
|
|
|
CEntityBatch::~CEntityBatch()
|
|
{
|
|
GetInstanceList()->ReleaseDeviceResources();
|
|
}
|
|
|
|
#if __BANK
|
|
template <class S, class T>
|
|
s32 CEntityBatchBase<S, T>::GetNumInstances()
|
|
{
|
|
return m_InstanceList->m_InstanceList.GetCount();
|
|
}
|
|
#endif
|
|
|
|
void CEntityBatch::InitEntityFromDefinition(const fwPropInstanceListDef *definition, fwArchetype *archetype, s32 mapDataDefIndex)
|
|
{
|
|
if( Verifyf(definition, "WARNING: Can't Initialize Entity Batch from NULL definition!") &&
|
|
Verifyf(archetype, "WARNING: Can't Initialize Entity Batch from NULL archetype!") )
|
|
{
|
|
//fwPropInstanceListDef parser data should be marked as retained, so it should be safe to just store off the pointer...
|
|
//Storing off map data index as well, which can later be used for validation purposes.
|
|
m_InstanceList = definition;
|
|
m_MapDataDefIndex = mapDataDefIndex;
|
|
|
|
//Initialize list's instance buffers
|
|
m_InstanceList->CreateDeviceResources();
|
|
|
|
//Cache off HD map data index
|
|
fwMapDataDef *pDef = g_MapDataStore.GetSlot(strLocalIndex(m_MapDataDefIndex));
|
|
if( Verifyf(pDef, "WARNING: CEntityBatch::InitEntityFromDefinition() - Trying to access owning mapdata (index: %d) but the slot does not appear to be valid!", m_MapDataDefIndex) &&
|
|
Verifyf(pDef->GetContents(), "WARNING: CEntityBatch::InitEntityFromDefinition() - Trying to access owning mapdata contents (index: %d) but the contents is NULL!", m_MapDataDefIndex) &&
|
|
Verifyf(pDef->GetContents()->GetLoaderInstRef().GetInstance(), "WARNING: CEntityBatch::InitEntityFromDefinition() - Trying to access owning mapdata ptr (index: %d) but it is NULL!", m_MapDataDefIndex) )
|
|
{
|
|
fwMapDataContents *contents = pDef->GetContents();
|
|
fwInstancedMapData &instList = reinterpret_cast<fwMapData *>(contents->GetLoaderInstRef().GetInstance())->m_instancedData;
|
|
m_HdMapDataDefIndex = g_MapDataStore.FindSlotFromHashKey(instList.m_ImapLink.GetHash()).Get();
|
|
Assertf(m_HdMapDataDefIndex >= 0, "WARNING! CEntityBatch::InitEntityFromDefinition() - Trying to cache of HD mapdata index for instance linking, but the system is unable to find"
|
|
"mapdata %s (0x%x) in mapdata store. Linking will fail.", instList.m_ImapLink.TryGetCStr() ? instList.m_ImapLink.TryGetCStr() : "<Unknown hash>", instList.m_ImapLink.GetHash());
|
|
}
|
|
|
|
//Setup LOD distance
|
|
SetLodDistance(EBStatic::sOverrideLodDist ? EBStatic::sOverriddenLodDistValue : m_InstanceList->m_lodDist); //Max LOD distance.
|
|
|
|
InitVisibilityFromDefinition(definition, archetype);
|
|
InitBatchTransformFromDefinition(definition, archetype);
|
|
|
|
//Do this here in case drawable was already created and we missed the call to setup the AO scales.
|
|
CalculateAmbientScales();
|
|
}
|
|
}
|
|
|
|
fwDrawData *CEntityBatch::AllocateDrawHandler(rmcDrawable *pDrawable)
|
|
{
|
|
//This is a convenient place to try and find the LOD to draw.
|
|
if(pDrawable && pDrawable->HasInstancedGeometry())
|
|
{
|
|
for(int lod = 0; lod < LOD_COUNT; ++lod)
|
|
{
|
|
if(pDrawable->GetLodGroup().IsLodInstanced(lod, pDrawable->GetShaderGroup()))
|
|
{
|
|
m_Lod = lod;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rage_new CEntityBatchDrawHandler(this, pDrawable);
|
|
}
|
|
|
|
void CEntityBatch::CalculateDynamicAmbientScales()
|
|
{
|
|
if(m_InstanceList) //Drawable can stream in before entity is initialized.
|
|
{
|
|
if(EBStatic::sComputeAoScalePerInstance)
|
|
{
|
|
fwInteriorLocation interiorLocation = GetInteriorLocation();
|
|
InstanceList::InstanceDataList::const_iterator end = m_InstanceList->m_InstanceList.end();
|
|
InstanceList::InstanceDataList::const_iterator iter;
|
|
for(iter = m_InstanceList->m_InstanceList.begin(); iter != end; ++iter)
|
|
{
|
|
const Vec2V vAmbientScale = g_timeCycle.CalcAmbientScale(iter->GetPosition(), interiorLocation);
|
|
Assertf(vAmbientScale.GetXf() >= 0.0f && vAmbientScale.GetXf() <= 1.0f,"Dynamic Natural Ambient is out of range (%f, should be between 0.0f and 1.0f)",vAmbientScale.GetXf());
|
|
Assertf(vAmbientScale.GetYf() >= 0.0f && vAmbientScale.GetYf() <= 1.0f,"Dynamic Artificial Ambient is out of range (%f, should be between 0.0f and 1.0f)",vAmbientScale.GetYf());
|
|
|
|
iter->SetGlobals(Vec4V(ScalarV(V_ONE), vAmbientScale.GetX(), vAmbientScale.GetY(), ScalarV(V_ONE)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parent_type::CalculateDynamicAmbientScales();
|
|
Vec4V globals(1.0f, CShaderLib::DivideBy255(GetNaturalAmbientScale()), CShaderLib::DivideBy255(GetArtificialAmbientScale()), 1.0f);
|
|
std::for_each(m_InstanceList->m_InstanceList.begin(), m_InstanceList->m_InstanceList.end(), std::bind2nd(std::mem_fun_ref(&InstanceList::InstanceData::SetGlobals), globals));
|
|
}
|
|
}
|
|
}
|
|
|
|
u32 CEntityBatch::UpdateLinkedInstanceVisibility() const
|
|
{
|
|
u32 count = 0;
|
|
if(m_InstanceList)
|
|
{
|
|
fwMapDataDef *pDef = (m_HdMapDataDefIndex > -1 ? g_MapDataStore.GetSlot(strLocalIndex(m_HdMapDataDefIndex)) : NULL);
|
|
if(pDef && pDef->IsLoaded() && pDef->GetContents() && EBStatic::sEnableLinkageVisibility)
|
|
{
|
|
fwMapDataContents *contents = pDef->GetContents();
|
|
fwEntity** entities = contents->GetEntities();
|
|
u32 numEntities = contents->GetNumEntities();
|
|
|
|
InstanceList::InstanceDataList::const_iterator end = m_InstanceList->m_InstanceList.end();
|
|
InstanceList::InstanceDataList::const_iterator iter;
|
|
for(iter = m_InstanceList->m_InstanceList.begin(); iter != end; ++iter)
|
|
{
|
|
if(iter->m_Index == 0xffffffff) //No linkage. Might not need to support later.
|
|
{
|
|
iter->m_IsVisible = true;
|
|
++count;
|
|
}
|
|
else if(Verifyf( iter->m_Index < numEntities, "WARNING! CEntityBatch::UpdateLinkedInstanceVisibility() - Instance %d has an invalid/out-of-bounds HD link index! [index = %d | max = %d]",
|
|
(int)(iter - m_InstanceList->m_InstanceList.begin()), iter->m_Index, numEntities ))
|
|
{
|
|
CEntity *entity = static_cast<CEntity *>(entities[iter->m_Index]);
|
|
if(entity && entity->GetAddedToDrawListThisFrame(DL_RENDERPHASE_GBUF))
|
|
{
|
|
iter->m_IsVisible = false;
|
|
}
|
|
else
|
|
{
|
|
iter->m_IsVisible = true;
|
|
++count;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//What should we draw in the case of an invalid linkage? (This would really be an asset issue, and we'll have already asserted thanks to the verify above.)
|
|
iter->m_IsVisible = true;
|
|
++count;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::for_each(m_InstanceList->m_InstanceList.begin(), m_InstanceList->m_InstanceList.end(), std::bind2nd(std::mem_fun_ref(&InstanceList::InstanceData::SetIsVisible), true));
|
|
count = m_InstanceList->m_InstanceList.size();
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
grcInstanceBuffer *CEntityBatch::CreateInstanceBuffer() const
|
|
{
|
|
u32 numVisible = UpdateLinkedInstanceVisibility();
|
|
|
|
if(m_InstanceList && numVisible > 0)
|
|
return m_InstanceList->CreateInstanceBuffer();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CEntityBatch::ResetHdEntityVisibility()
|
|
{
|
|
EBStatic::ResetHdEntityVisibility();
|
|
}
|
|
|
|
#if __BANK
|
|
void CEntityBatch::RenderDebug()
|
|
{
|
|
CCustomShaderEffectGrass::RenderDebug();
|
|
CEntityBatchDrawHandler::RenderDebug();
|
|
EBStatic::RenderDebug();
|
|
}
|
|
|
|
void CEntityBatch::AddWidgets(bkBank &bank)
|
|
{
|
|
bank.PushGroup("Instance Lists", false);
|
|
{
|
|
CEntityBatchDrawHandler::AddWidgets(bank);
|
|
|
|
//Debug Draw
|
|
EBStatic::AddWidgets(bank);
|
|
}
|
|
bank.PopGroup();
|
|
}
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Grass Batch Implementation
|
|
//
|
|
#include "streaming/streamingengine.h"
|
|
|
|
#define USE_CONSTANT_BUFFER (__D3D11)
|
|
|
|
namespace EBStatic
|
|
{
|
|
struct SharedVertex
|
|
{
|
|
Mat33V Matrix;
|
|
};
|
|
|
|
#if RSG_PC && USE_CONSTANT_BUFFER
|
|
static grcCBuffer *sCB = NULL;
|
|
#elif RSG_DURANGO
|
|
static char *sSharedData = NULL;
|
|
#elif RSG_ORBIS
|
|
static void *sSharedData = NULL;
|
|
static sce::Gnm::Buffer sCB;
|
|
#else
|
|
static grcVertexBuffer *sSharedVerts = NULL;
|
|
static grcFvf sSharedVertFvf;
|
|
static const u32 sSharedVertStream = 2;
|
|
#endif
|
|
|
|
//PS3 Rendering implementation relies on GRASS_BATCH_SHARED_DATA_COUNT to match the number of times the instance buffer was duplicated in ragebuilder.
|
|
PS3_ONLY(CompileTimeAssert(grcInstanceBuffer::MaxPerDraw == GRASS_BATCH_SHARED_DATA_COUNT));
|
|
|
|
#if (RSG_PC && USE_CONSTANT_BUFFER) || RSG_DURANGO || RSG_ORBIS
|
|
//Make sure our buffers are the expected size from the shader!
|
|
static const u32 sSharedCBSize = sizeof(SharedVertex[GRASS_BATCH_SHARED_DATA_COUNT]);
|
|
CompileTimeAssert(sSharedCBSize == GRASS_BATCH_NUM_CONSTS_PER_INSTANCE * GRASS_BATCH_SHARED_DATA_COUNT * 16);
|
|
#endif
|
|
|
|
bool CreateSharedBuffer()
|
|
{
|
|
bool success = true;
|
|
|
|
#if (RSG_PC && USE_CONSTANT_BUFFER) || RSG_DURANGO || RSG_ORBIS
|
|
SharedVertex *sharedData = NULL;
|
|
|
|
#if RSG_PC && USE_CONSTANT_BUFFER
|
|
u16 registers[NONE_TYPE] = { 0 };
|
|
registers[VS_TYPE] = (u16)INSTANCE_CONSTANT_BUFFER_SLOT;
|
|
|
|
sCB = rage_new rage::grcCBuffer(sSharedCBSize, false);
|
|
sCB->SetRegisters(registers);
|
|
GRCDEVICE.LockContext();
|
|
sharedData = static_cast<SharedVertex *>(sCB->BeginUpdate(sSharedCBSize));
|
|
#elif RSG_DURANGO
|
|
sSharedData = rage_aligned_new(16) char[sSharedCBSize];
|
|
sharedData = (SharedVertex*)sSharedData;
|
|
#elif RSG_ORBIS
|
|
sSharedData = allocateVideoSharedMemory(sSharedCBSize, 16);
|
|
sCB.initAsConstantBuffer(sSharedData, sSharedCBSize);
|
|
sharedData = static_cast<SharedVertex *>(sSharedData);
|
|
#endif
|
|
|
|
if(Verifyf(sharedData, "Failed to get buffer lock to write out shared data?"))
|
|
{
|
|
for(u32 i = 0; i < GRASS_BATCH_SHARED_DATA_COUNT; ++i)
|
|
{
|
|
//Setup randomized rotation
|
|
static const ScalarV angleStep = ScalarV(V_TWO_PI) / ScalarV(static_cast<float>(GRASS_BATCH_SHARED_DATA_COUNT));
|
|
const ScalarV angle = angleStep * ScalarV(static_cast<float>(i));
|
|
Mat33VFromAxisAngle(sharedData[i].Matrix, Vec3V(V_Z_AXIS_WZERO), angle);
|
|
|
|
//Setup randomized scale
|
|
static const ScalarV scaleStep = ScalarV(V_ONE) / ScalarV(static_cast<float>(GRASS_BATCH_SHARED_DATA_COUNT - 1));
|
|
sharedData[i].Matrix.GetCol0Ref().SetW(scaleStep * ScalarV(static_cast<float>(i)));
|
|
}
|
|
}
|
|
else
|
|
success = false;
|
|
|
|
#if RSG_PC && USE_CONSTANT_BUFFER
|
|
sCB->EndUpdate();
|
|
GRCDEVICE.UnlockContext();
|
|
#endif
|
|
#else
|
|
sSharedVertFvf.ClearAllChannels();
|
|
sSharedVertFvf.SetTextureChannel(2, true, grcFvf::grcdsFloat4);
|
|
sSharedVertFvf.SetTextureChannel(3, true, grcFvf::grcdsFloat4);
|
|
sSharedVertFvf.SetTextureChannel(4, true, grcFvf::grcdsFloat4);
|
|
|
|
const bool createReadWrite = true;
|
|
sSharedVerts = grcVertexBuffer::Create(GRASS_BATCH_SHARED_DATA_COUNT, sSharedVertFvf, createReadWrite, false, NULL);
|
|
if(sSharedVerts)
|
|
{
|
|
{
|
|
//not resourcing this currently
|
|
PS3_ONLY(g_AllowVertexBufferVramLocks++);
|
|
|
|
grcVertexBuffer::LockHelper lock(sSharedVerts);
|
|
if(SharedVertex *vertLock = reinterpret_cast<SharedVertex *>(lock.GetLockPtr()))
|
|
{
|
|
for(u32 i = 0; i < GRASS_BATCH_SHARED_DATA_COUNT; ++i)
|
|
{
|
|
//Setup randomized rotation
|
|
static const ScalarV angleStep = ScalarV(V_TWO_PI) / ScalarV(static_cast<float>(GRASS_BATCH_SHARED_DATA_COUNT));
|
|
const ScalarV angle = angleStep * ScalarV(static_cast<float>(i));
|
|
Mat33VFromAxisAngle(vertLock[i].Matrix, Vec3V(V_Z_AXIS_WZERO), angle);
|
|
|
|
//Setup randomized scale
|
|
static const ScalarV scaleStep = ScalarV(V_ONE) / ScalarV(static_cast<float>(GRASS_BATCH_SHARED_DATA_COUNT - 1));
|
|
vertLock[i].Matrix.GetCol0Ref().SetW(scaleStep * ScalarV(static_cast<float>(i)));
|
|
}
|
|
}
|
|
|
|
PS3_ONLY(g_AllowVertexBufferVramLocks--);
|
|
}
|
|
|
|
sSharedVerts->MakeReadOnly();
|
|
}
|
|
else
|
|
success = false;
|
|
#endif
|
|
|
|
return success;
|
|
}
|
|
|
|
void DestorySharedBuffer()
|
|
{
|
|
#if RSG_PC && USE_CONSTANT_BUFFER
|
|
delete sCB;
|
|
sCB = NULL;
|
|
#elif RSG_DURANGO
|
|
delete[] sSharedData;
|
|
sSharedData = NULL;
|
|
#elif RSG_ORBIS
|
|
sCB = sce::Gnm::Buffer(); //Is this the best way to reset a sce::Gnm::Buffer??
|
|
freeVideoSharedMemory(sSharedData);
|
|
sSharedData = NULL;
|
|
#else
|
|
delete sSharedVerts;
|
|
#endif
|
|
}
|
|
|
|
bool BindSharedBuffer()
|
|
{
|
|
bool success = true;
|
|
|
|
#if RSG_PC && USE_CONSTANT_BUFFER
|
|
if(grcCBuffer *pCBuffer = sCB)
|
|
{
|
|
ID3D11Buffer *pD3DBuffer = pCBuffer->GetBuffer();
|
|
g_grcCurrentContext->VSSetConstantBuffers(INSTANCE_CONSTANT_BUFFER_SLOT, 1, &pD3DBuffer);
|
|
g_grcCurrentContext->CSSetConstantBuffers(INSTANCE_CONSTANT_BUFFER_SLOT, 1, &pD3DBuffer);
|
|
}
|
|
else
|
|
success = false;
|
|
#elif RSG_DURANGO
|
|
grcContextHandle *const ctx = g_grcCurrentContext;
|
|
grcDevice::SetConstantBuffer<VS_TYPE>(ctx, INSTANCE_CONSTANT_BUFFER_SLOT, sSharedData);
|
|
grcDevice::SetConstantBuffer<CS_TYPE>(ctx, INSTANCE_CONSTANT_BUFFER_SLOT, sSharedData);
|
|
#elif RSG_ORBIS
|
|
if(grcVertexProgram::GetCurrent())
|
|
gfxc.setConstantBuffers(grcVertexProgram::GetCurrent()->GetGnmStage(), INSTANCE_CONSTANT_BUFFER_SLOT, 1, &sCB);
|
|
|
|
gfxc.setConstantBuffers(sce::Gnm::kShaderStageCs, INSTANCE_CONSTANT_BUFFER_SLOT, 1, &sCB);
|
|
#else
|
|
GRCDEVICE.SetStreamSource(sSharedVertStream, *sSharedVerts, 0, sSharedVerts->GetVertexStride());
|
|
success = (sSharedVerts != NULL);
|
|
#endif
|
|
|
|
return success;
|
|
}
|
|
|
|
void SetGrassBatchVertexDeclarations(rmcDrawable *pDrawable)
|
|
{
|
|
if(pDrawable)
|
|
{
|
|
rmcLodGroup &lodGrp = pDrawable->GetLodGroup();
|
|
|
|
for(int lodIdx = 0; lodIdx < LOD_COUNT; ++lodIdx)
|
|
{
|
|
if(lodGrp.ContainsLod(lodIdx))
|
|
{
|
|
const rmcLod &lod = lodGrp.GetLod(lodIdx);
|
|
for(int i = 0; i < lod.GetCount(); ++i)
|
|
{
|
|
if(grmModel *model = lod.GetModel(i))
|
|
{
|
|
int count = model->GetGeometryCount();
|
|
for(int index = 0; index < count; ++index)
|
|
{
|
|
// #if __ASSERT
|
|
// static const char *sGrassBatchShaderName = "grass_batch";
|
|
// const grmShaderGroup &shaderGroup = pDrawable->GetShaderGroup();
|
|
// const int shaderIndex = model->GetShaderIndex(index);
|
|
// Assertf(!stricmp(sGrassBatchShaderName, shaderGroup.GetShader(shaderIndex).GetName()),
|
|
// "WARNING: Found non grass_batch shader \"%s\" in grass model \"%s\" LOD chain! [LOD: %d]",
|
|
// shaderGroup.GetShader(shaderIndex).GetName(), pDrawable->GetDebugName(NULL, 0), lodIdx);
|
|
// #endif
|
|
|
|
grmGeometry &geometry = model->GetGeometry(index);
|
|
grcVertexDeclaration *decl = NULL;
|
|
#if __XENON
|
|
if(const grcVertexBuffer *indexVerts = geometry.GetVertexBufferByIndex(3))
|
|
{
|
|
grcFvf instFvf = CGrassBatch::InstanceList::GetFVF();
|
|
decl = grmModelFactory::BuildDeclarator(geometry.GetFvf(), &instFvf, &sSharedVertFvf, indexVerts->GetFvf());
|
|
}
|
|
#else //__XENON
|
|
PS3_ONLY(u32 indexCount = geometry.GetIndexCount());
|
|
typedef atRangeArray<grcVertexElement, grmModelFactory::MaxVertElements> ElementArray;
|
|
ElementArray elements;
|
|
atRangeArray<int, grcVertexElement::grcvetCount> elemCounts;
|
|
grcFvf *sharedFvf =
|
|
#if (RSG_PC && USE_CONSTANT_BUFFER) || RSG_DURANGO || RSG_ORBIS
|
|
NULL;
|
|
#else
|
|
&sSharedVertFvf;
|
|
#endif
|
|
#if GRASS_BATCH_CS_CULLING
|
|
grcFvf *pInstFvf = NULL;
|
|
#else //GRASS_BATCH_CS_CULLING
|
|
grcFvf instFvf = CGrassBatch::InstanceList::GetFVF();
|
|
grcFvf *pInstFvf = &instFvf;
|
|
#endif //GRASS_BATCH_CS_CULLING
|
|
int numElem = grmModelFactory::ComputeVertexElements(geometry.GetFvf(), pInstFvf, sharedFvf, NULL, elements, elemCounts);
|
|
|
|
//Need to add appropriate instance modes & divide values before building the final declarator
|
|
for(u32 elem = 0; elem < numElem; ++elem)
|
|
{
|
|
#if __PS3
|
|
elements[elem].streamFrequencyMode = (elements[elem].stream != 0) ? grcFvf::grcsfmDivide : grcFvf::grcsfmModulo;
|
|
elements[elem].streamFrequencyDivider = indexCount;
|
|
#else //D3D11 || RSG_ORBIS
|
|
if(elements[elem].stream != 0)
|
|
{
|
|
elements[elem].streamFrequencyMode = grcFvf::grcsfmIsInstanceStream;
|
|
elements[elem].streamFrequencyDivider = 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
decl = grmModelFactory::BuildCachedDeclarator(geometry.GetFvf(), pInstFvf, sharedFvf, NULL, elements, numElem);
|
|
#endif //__XENON
|
|
|
|
geometry.SetCustomDecl(decl);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if GRASS_BATCH_CS_DYNAMIC_BUFFERS
|
|
u32 ComputeIdirectArgCount(const rmcDrawable *drawable, atRangeArray<u32, LOD_COUNT> &lodOffsets)
|
|
{
|
|
u32 count = 0;
|
|
if(drawable)
|
|
{
|
|
const rmcLodGroup &lodGrp = drawable->GetLodGroup();
|
|
for(int lodIdx = 0; lodIdx < LOD_COUNT; ++lodIdx)
|
|
{
|
|
lodOffsets[lodIdx] = count;
|
|
if(lodGrp.ContainsLod(lodIdx))
|
|
{
|
|
const rmcLod &lod = lodGrp.GetLod(lodIdx);
|
|
for(int modelIdx = 0; modelIdx < lod.GetCount(); ++modelIdx)
|
|
{
|
|
const grmModel *model = lod.GetModel(modelIdx);
|
|
if(model)
|
|
{
|
|
count += static_cast<u32>(model->GetGeometryCount());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
PF_PAGE(CGrassBatch, "Grass Batch profiling");
|
|
PF_GROUP(IndirectArgCritSec);
|
|
PF_LINK(CGrassBatch, IndirectArgCritSec);
|
|
PF_COUNTER_CUMULATIVE_FLOAT(AddToMapCritSecTimeMs, IndirectArgCritSec);
|
|
PF_COUNTER_CUMULATIVE_FLOAT(RemoveFromMapCritSecTimeMs, IndirectArgCritSec);
|
|
PF_COUNTER_CUMULATIVE_FLOAT(PlaceDrawableCritSecTimeMs, IndirectArgCritSec);
|
|
PF_COUNTER_CUMULATIVE_FLOAT(CumulativeCritSecTimeMs, IndirectArgCritSec);
|
|
|
|
PF_GROUP(DeviceResourceCreation);
|
|
PF_LINK(CGrassBatch, DeviceResourceCreation);
|
|
PF_COUNTER_CUMULATIVE_FLOAT(IndirectArgBuffersSec, DeviceResourceCreation);
|
|
PF_COUNTER_CUMULATIVE_FLOAT(StructuredBuffersSec, DeviceResourceCreation);
|
|
#endif //GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
|
|
namespace EBStatic
|
|
{
|
|
#if GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
DrawableDeviceResources::DrawableDeviceResources(rmcDrawable &drawable)
|
|
: m_IndirectArgsBuffer(ORBIS_ONLY(grcBuffer_Raw, true))
|
|
{
|
|
u32 indirectArgCount = 0;
|
|
rmcLodGroup &lodGrp = drawable.GetLodGroup();
|
|
for(int lodIdx = 0; lodIdx < LOD_COUNT; ++lodIdx)
|
|
{
|
|
if(lodGrp.ContainsLod(lodIdx) && lodGrp.GetLod(lodIdx).GetCount() > 0)
|
|
{
|
|
rmcLod &lod = lodGrp.GetLod(lodIdx);
|
|
m_OffsetMap[lodIdx].Resize(lod.GetCount());
|
|
for(int modelIdx = 0; modelIdx < lod.GetCount(); ++modelIdx)
|
|
{
|
|
if(grmModel *model = lod.GetModel(modelIdx))
|
|
{
|
|
m_OffsetMap[lodIdx][modelIdx].Resize(model->GetGeometryCount());
|
|
for(int geoIdx = 0; geoIdx < model->GetGeometryCount(); ++geoIdx)
|
|
m_OffsetMap[lodIdx][modelIdx][geoIdx] = sizeof(IndirectArgParams) * indirectArgCount++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Now we know indirect arg count... we need an initial value buffer.
|
|
IndirectArgParams *bufferInit = rage_aligned_new(16) IndirectArgParams[indirectArgCount];
|
|
u32 currentArg = 0;
|
|
for(int lodIdx = 0; lodIdx < LOD_COUNT; ++lodIdx)
|
|
{
|
|
if(lodGrp.ContainsLod(lodIdx) && lodGrp.GetLod(lodIdx).GetCount() > 0)
|
|
{
|
|
rmcLod &lod = lodGrp.GetLod(lodIdx);
|
|
for(int modelIdx = 0; modelIdx < lod.GetCount(); ++modelIdx)
|
|
{
|
|
if(grmModel *model = lod.GetModel(modelIdx))
|
|
{
|
|
for(int geoIdx = 0; geoIdx < model->GetGeometryCount(); ++geoIdx)
|
|
bufferInit[currentArg++].m_indexCountPerInstance = model->GetGeometry(geoIdx).GetIndexCount();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Assert(indirectArgCount == currentArg); //Make sure our counts match!
|
|
|
|
//Lastly, create the device resource.
|
|
#if RSG_ORBIS
|
|
u32 indirectArgSize = sizeof(IndirectArgParams) * indirectArgCount;
|
|
const sce::Gnm::SizeAlign indirectSizeAlign = { indirectArgSize, sce::Gnm::kAlignmentOfIndirectArgsInBytes };
|
|
m_IndirectArgsBuffer.Allocate(indirectSizeAlign, true, bufferInit);
|
|
#elif RSG_DURANGO
|
|
u32 indirectArgSize = sizeof(IndirectArgParams) * indirectArgCount;
|
|
GRCDEVICE.LockContext();
|
|
m_IndirectArgsBuffer.Initialise(indirectArgSize, 1, grcBindShaderResource, NULL, grcBufferMisc_DrawIndirectArgs);
|
|
u32 *pDest = static_cast<u32 *>(uav.Lock(grcsWrite, 0, 0, NULL));
|
|
memcpy(pDest, bufferInit, indirectArgSize);
|
|
uav.Unlock(grcsWrite);
|
|
GRCDEVICE.UnlockContext();
|
|
#else // RSG_PC
|
|
m_IndirectArgsBuffer.Initialise(indirectArgCount, sizeof(IndirectArgParams), grcBindNone, grcsBufferCreate_ReadWrite, grcsBufferSync_None, bufferInit, false, grcBufferMisc_DrawIndirectArgs);
|
|
#endif
|
|
|
|
delete[] bufferInit;
|
|
}
|
|
|
|
void PlaceDrawableIndirectArgs(strLocalIndex drawableIdx, rmcDrawable *drawable)
|
|
{
|
|
//Only add indirect args if the drawable has a compute shader?
|
|
bool hasComputeShader = false;
|
|
if(drawable)
|
|
{
|
|
grmShaderGroup &shaderGrp = drawable->GetShaderGroup();
|
|
for(int i = 0; i < shaderGrp.GetCount(); ++i)
|
|
{
|
|
grmShader &shader = shaderGrp.GetShader(i);
|
|
if(shader.GetComputeProgram(0) != NULL)
|
|
{
|
|
hasComputeShader = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!hasComputeShader)
|
|
return;
|
|
|
|
fwDrawableDef *drawableDef = g_DrawableStore.GetSlot(drawableIdx);
|
|
if(Verifyf(drawableDef, "Invalid drawable index being placed??") && Verifyf(drawable, "NULL drawable was just placed??"))
|
|
{
|
|
if(drawableDef->m_deviceResources)
|
|
return; //Already been created previously? Should always be correct once created... so just return.
|
|
|
|
DrawableDeviceResources *drawableDR = rage_new DrawableDeviceResources(*drawable);
|
|
drawableDef->m_deviceResources = reinterpret_cast<void *>(drawableDR);
|
|
}
|
|
}
|
|
|
|
void RemoveDrawableIndirectArgs(strLocalIndex drawableIdx)
|
|
{
|
|
fwDrawableDef *drawableDef = g_DrawableStore.GetSlot(drawableIdx);
|
|
if(Verifyf(drawableDef, "Invalid drawable index being removed??"))
|
|
{
|
|
delete reinterpret_cast<EBStatic::DrawableDeviceResources *>(drawableDef->m_deviceResources);
|
|
drawableDef->m_deviceResources = NULL;
|
|
}
|
|
}
|
|
#endif //GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
|
|
void *CreateDeviceResources(fwGrassInstanceListDef &batch)
|
|
{
|
|
return rage_new CGrassBatch::DeviceResources(batch);
|
|
}
|
|
|
|
void ReleaseDeviceResources(void *dr)
|
|
{
|
|
CGrassBatch::DeviceResources *deviceRsc = static_cast<CGrassBatch::DeviceResources *>(dr);
|
|
delete deviceRsc;
|
|
}
|
|
}
|
|
|
|
CGrassBatch::DeviceResources::DeviceResources(fwGrassInstanceListDef &batch)
|
|
#if GRASS_BATCH_CS_CULLING
|
|
: m_RawInstBuffer(ORBIS_ONLY(grcBuffer_Structured, false))
|
|
#else //GRASS_BATCH_CS_CULLING
|
|
: m_Verts(NULL)
|
|
#endif //GRASS_BATCH_CS_CULLING
|
|
{
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(STATS_ONLY(sysTimer indirectArgTimer));
|
|
#if GRASS_BATCH_CS_CULLING
|
|
# if RSG_ORBIS
|
|
m_RawInstBuffer.Initialize(static_cast<void *>(batch.m_InstanceList.GetElements()), batch.m_InstanceList.GetCount(), false, sizeof(CGrassBatch::InstanceList::InstanceData));
|
|
# elif RSG_DURANGO
|
|
//GRCDEVICE.LockContext(); //No longer necessary?
|
|
m_RawInstBuffer.Initialise(batch.m_InstanceList.GetCount(), sizeof(CGrassBatch::InstanceList::InstanceData), grcBindShaderResource|grcBindUnorderedAccess, static_cast<void *>(batch.m_InstanceList.GetElements()), grcBufferMisc_BufferStructured);
|
|
//GRCDEVICE.UnlockContext();
|
|
# else // RSG_PC
|
|
if(GRCDEVICE.SupportsFeature(COMPUTE_SHADER_50))
|
|
m_RawInstBuffer.Initialise(batch.m_InstanceList.GetCount(), sizeof(CGrassBatch::InstanceList::InstanceData), grcBindShaderResource|grcBindUnorderedAccess, grcsBufferCreate_ReadWriteOnceOnly, grcsBufferSync_None, static_cast<void *>(batch.m_InstanceList.GetElements()), false, grcBufferMisc_BufferStructured);
|
|
# endif
|
|
#else //GRASS_BATCH_CS_CULLING
|
|
const bool bReadWrite = false;
|
|
m_Verts = grcVertexBuffer::Create(batch.m_InstanceList.size(), fwGrassInstanceListDef::GetFVF(), bReadWrite, false, static_cast<void *>(batch.m_InstanceList.GetElements()));
|
|
#endif //GRASS_BATCH_CS_CULLING
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(PF_INCREMENTBY(StructuredBuffersSec, indirectArgTimer.GetTime()));
|
|
}
|
|
|
|
CGrassBatch::DeviceResources::~DeviceResources()
|
|
{
|
|
#if GRASS_BATCH_CS_CULLING
|
|
#else
|
|
delete m_Verts;
|
|
m_Verts = NULL;
|
|
#endif
|
|
}
|
|
|
|
CGrassBatch::CGrassBatch(const eEntityOwnedBy ownedBy)
|
|
: parent_type(ownedBy)
|
|
, m_DistToCamera(0.0f)
|
|
{
|
|
SetTypeGrassInstanceList();
|
|
}
|
|
|
|
CGrassBatch::~CGrassBatch()
|
|
{
|
|
GetInstanceList()->ReleaseDeviceResources();
|
|
}
|
|
|
|
void CGrassBatch::Init(unsigned initMode)
|
|
{
|
|
if(initMode == INIT_CORE)
|
|
{
|
|
#if RSG_PC
|
|
if(CGrassBatch::IsRenderingEnabled())
|
|
#endif
|
|
{
|
|
//Setup shared vertex data
|
|
ASSERT_ONLY(bool success = ) EBStatic::CreateSharedBuffer();
|
|
Assertf(success, "Failed to create grass shared buffer!");
|
|
|
|
fwGrassInstanceListDef::Init(&EBStatic::CreateDeviceResources, &EBStatic::ReleaseDeviceResources);
|
|
#if GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
if(GRCDEVICE.SupportsFeature(COMPUTE_SHADER_50))
|
|
{
|
|
const float maxLodScale = CSettingsManager::GetInstance().GetSettings().m_graphics.m_MaxLodScale;
|
|
const float delta = maxLodScale; // Max Lod Scale goes from 0.0f to 1.0f
|
|
int appendBufferPreAllocateCount = Lerp(delta,(int)EBStatic::PreAllocatedAppendBufferPool::sDefaultInitialPreAllocatedCount,4750);
|
|
int sIndirectBufferPreAllocateCount = Lerp(delta,(int)EBStatic::PreAllocatedAppendBufferPool::sDefaultInitialPreAllocatedCount,4250);
|
|
|
|
fwGrassInstanceListDef::SetDrawablePlacementNotifyFuncs(&EBStatic::PlaceDrawableIndirectArgs, & EBStatic::RemoveDrawableIndirectArgs);
|
|
EBStatic::sAppendBufferPool.PreAllocateResources(appendBufferPreAllocateCount);
|
|
GRCDEVICE.LockContext();
|
|
EBStatic::sIndirectArgBufferPool.PreAllocateResources(sIndirectBufferPreAllocateCount);
|
|
GRCDEVICE.UnlockContext();
|
|
}
|
|
#endif //GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
}
|
|
}
|
|
|
|
BANK_ONLY(EBStatic::Init(initMode));
|
|
}
|
|
|
|
void CGrassBatch::Shutdown()
|
|
{
|
|
EBStatic::DestorySharedBuffer();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Grass Settings
|
|
//
|
|
// Custom: No grass rendering at all.
|
|
// Low: Grass renders only in gbuffer (w/ low lod?)
|
|
// Medium: Grass renders gbuffer/shadow pass. (w/ low lod in shadows?)
|
|
// High: Normal grass rendering.
|
|
// Ultra: High + Force shadows on for all grass and set some min fade distance
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void CGrassBatch::SetQuality(CSettings::eSettingsLevel quality)
|
|
{
|
|
EBStatic::sQuality = quality;
|
|
|
|
switch(quality)
|
|
{
|
|
case CSettings::Low: // No Grass
|
|
{
|
|
CRenderPhaseCascadeShadowsInterface::SetGrassEnabled(false);
|
|
g_LodScale.SetGrassQualityScale(1.0f);
|
|
break;
|
|
}
|
|
case CSettings::Medium: // No Grass in shadows
|
|
{
|
|
CRenderPhaseCascadeShadowsInterface::SetGrassEnabled(false);
|
|
g_LodScale.SetGrassQualityScale(1.0f);
|
|
break;
|
|
}
|
|
case CSettings::High: // Some Grass in shadows
|
|
{
|
|
CRenderPhaseCascadeShadowsInterface::SetGrassEnabled(true);
|
|
g_LodScale.SetGrassQualityScale(1.0f);
|
|
break;
|
|
}
|
|
case CSettings::Ultra: // All the grass everywhere
|
|
{
|
|
CRenderPhaseCascadeShadowsInterface::SetGrassEnabled(true);
|
|
g_LodScale.SetGrassQualityScale(1.6f);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
CRenderPhaseCascadeShadowsInterface::SetGrassEnabled(false);
|
|
g_LodScale.SetGrassQualityScale(1.0f);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CGrassBatch::IsRenderingEnabled()
|
|
{
|
|
return (EBStatic::sQuality != CSettings::Low) && (GRCDEVICE.GetDxFeatureLevel() >= 1100);
|
|
}
|
|
|
|
int CGrassBatch::GetSharedInstanceCount()
|
|
{
|
|
return GRASS_BATCH_SHARED_DATA_COUNT;
|
|
}
|
|
|
|
bool CGrassBatch::BindSharedBuffer()
|
|
{
|
|
return EBStatic::BindSharedBuffer();
|
|
}
|
|
|
|
void CGrassBatch::InitEntityFromDefinition(fwGrassInstanceListDef *definition, fwArchetype *archetype, s32 mapDataDefIndex)
|
|
{
|
|
if( Verifyf(definition, "WARNING: Can't Initialize Entity Batch from NULL definition!") &&
|
|
Verifyf(archetype, "WARNING: Can't Initialize Entity Batch from NULL archetype!") )
|
|
{
|
|
//fwGrassInstanceListDef parser data should be marked as retained, so it should be safe to just store off the pointer...
|
|
//Storing off map data index as well, which can later be used for validation purposes.
|
|
m_InstanceList = definition;
|
|
m_MapDataDefIndex = mapDataDefIndex;
|
|
|
|
#if GRASS_BATCH_CS_CULLING
|
|
//Setup LOD distance - Must be large enough to cover LOD fade for farthest element.
|
|
ScalarV minLodDist = Mag(definition->m_BatchAABB.GetExtent());
|
|
float lodDist = minLodDist.Getf() + m_InstanceList->m_lodDist;
|
|
#endif //#if GRASS_BATCH_CS_CULLING
|
|
SetLodDistance(EBStatic::sOverrideLodDist ? EBStatic::sOverriddenLodDistValue : GRASS_BATCH_CS_CULLING_SWITCH(static_cast<u32>(lodDist), m_InstanceList->m_lodDist)); //Max LOD distance.
|
|
|
|
InitVisibilityFromDefinition(definition, archetype);
|
|
InitBatchTransformFromDefinition(definition, archetype);
|
|
|
|
if(EBStatic::sQuality == CSettings::Ultra)
|
|
{
|
|
fwFlags16 &visibilityMask = GetRenderPhaseVisibilityMask();
|
|
visibilityMask.SetFlag(VIS_PHASE_MASK_SHADOWS);
|
|
}
|
|
|
|
if(m_InstanceList->GetDeviceResources() == NULL)
|
|
{
|
|
#if !__GAMETOOL
|
|
Warningf("Grass data needs to be rebuilt to avoid stalls on PC");
|
|
#endif
|
|
PF_AUTO_PUSH_TIMEBAR_BUDGETED("CreateDeviceResources", 0.4f);
|
|
m_InstanceList->CreateDeviceResources();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
ePrerenderStatus CGrassBatch::PreRender(const bool bIsVisibleInMainViewport)
|
|
{
|
|
//Must compute distance to camera before calling parent's pre-render! This value is used by the CSE's update.
|
|
spdAABB box;
|
|
GetAABB(box);
|
|
Vec3V camPos = VECTOR3_TO_VEC3V(camInterface::GetPos());
|
|
m_DistToCamera = box.DistanceToPoint(camPos).Getf();
|
|
|
|
return parent_type::PreRender(bIsVisibleInMainViewport);
|
|
}
|
|
|
|
fwDrawData *CGrassBatch::AllocateDrawHandler(rmcDrawable *pDrawable)
|
|
{
|
|
//Good place to setup the vertex declaration?
|
|
EBStatic::SetGrassBatchVertexDeclarations(pDrawable);
|
|
|
|
#if GRASS_BATCH_CS_CULLING
|
|
//Reset cs params?
|
|
m_CSParams = EBStatic::GrassCSBaseParams();
|
|
|
|
m_CSParams.m_InstanceCount = m_InstanceList->m_InstanceList.GetCount();
|
|
m_CSParams.m_InstanceList = GetInstanceList();
|
|
GRASS_BATCH_CS_DYNAMIC_BUFFERS_ONLY(m_CSParams.m_IndirectArgCount = EBStatic::ComputeIdirectArgCount(pDrawable , m_CSParams.m_IndirectArgLodOffsets));
|
|
|
|
CBaseModelInfo *pModelInfo = GetBaseModelInfo();
|
|
CCustomShaderEffectBaseType *cseType = pModelInfo ? pModelInfo->GetCustomShaderEffectType() : NULL;
|
|
if( Verifyf(cseType, "Could not get CSE type! Entity Name: %s | Drawable Name: %s | pModelInfo = 0x%p", GetModelName(), pDrawable ? pDrawable->GetDebugName(NULL, 0) : "<NULL Drawable>", pModelInfo) &&
|
|
Verifyf(cseType->GetType() == CSE_GRASS, "CSE type is not grass! Entity Name: %s | Drawable Name: %s | type = %d", GetModelName(), pDrawable ? pDrawable->GetDebugName(NULL, 0) : "<NULL Drawable>", cseType->GetType()))
|
|
{
|
|
m_CSParams.m_Vars = &(static_cast<CCustomShaderEffectGrassType *>(cseType)->GetCSVars());
|
|
artAssertf(m_CSParams.m_Vars->m_ShaderIndex != -1, "Batch drawable found that does not appear to have a valid batch shader! Batch will not render! Entity Name: %s | Drawable Name: %s", GetModelName(), pDrawable ? pDrawable->GetDebugName(NULL, 0) : "<NULL Drawable>");
|
|
}
|
|
|
|
if(GetArchetype())
|
|
{
|
|
const spdAABB &instAabb = GetArchetype()->GetBoundingBox();
|
|
m_CSParams.m_InstAabbRadius = Mag(instAabb.GetMax() - instAabb.GetMin()).Getf();
|
|
}
|
|
|
|
const DeviceResources *dr = GetDeviceResources();
|
|
m_CSParams.m_RawInstBuffer = dr ? &(dr->m_RawInstBuffer) : NULL;
|
|
|
|
#if GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
strLocalIndex drawableIndex;
|
|
if(const fwArchetype *archetype = GetArchetype())
|
|
{
|
|
switch(archetype->GetDrawableType())
|
|
{
|
|
case fwArchetype::DT_DRAWABLE:
|
|
drawableIndex = strLocalIndex(archetype->GetDrawableIndex());
|
|
break;
|
|
|
|
case fwArchetype::DT_FRAGMENT:
|
|
case fwArchetype::DT_DRAWABLEDICTIONARY:
|
|
default:
|
|
Assertf(false, "WARNING: Grass batch drawable is an unsupported type: %d", archetype->GetDrawableType());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(drawableIndex.IsValid())
|
|
{
|
|
if(fwDrawableDef *drawableDef = g_DrawableStore.GetSlot(drawableIndex))
|
|
{
|
|
if(EBStatic::DrawableDeviceResources *ddr = reinterpret_cast<EBStatic::DrawableDeviceResources *>(drawableDef->m_deviceResources))
|
|
{
|
|
m_CSParams.m_IndirectArgsBuffer = &(ddr->m_IndirectArgsBuffer);
|
|
m_CSParams.m_OffsetMap = &(ddr->m_OffsetMap);
|
|
}
|
|
}
|
|
}
|
|
#endif //GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
|
|
PIX_TAGGING_ONLY(m_CSParams.m_ModelIndex = GetModelIndex());
|
|
|
|
m_CSParams.UpdateValidity();
|
|
Assert(m_CSParams.IsValid() WIN32PC_ONLY(|| !GRCDEVICE.SupportsFeature(COMPUTE_SHADER_50)));
|
|
#endif //GRASS_BATCH_CS_CULLING
|
|
|
|
CGrassBatchDrawHandler *dh = rage_new CGrassBatchDrawHandler(this, pDrawable);
|
|
return dh;
|
|
}
|
|
|
|
u32 CGrassBatch::ComputeLod(float dist) const
|
|
{
|
|
u32 LODIdx = 0;
|
|
if(CBaseModelInfo *pModelInfo = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(GetModelIndex()))))
|
|
{
|
|
if(rmcDrawable *pDrawable = pModelInfo->GetDrawable())
|
|
{
|
|
const bool bUseAltfadeDist = GetUseAltFadeDistance();
|
|
LODDrawable::crossFadeDistanceIdx fadeDist = bUseAltfadeDist ? LODDrawable::CFDIST_ALTERNATE : LODDrawable::CFDIST_MAIN;
|
|
u32 phaseLODs = GetPhaseLODs();
|
|
u32 lastLODIdx = GetLastLODIdx();
|
|
int crossfade = -1;
|
|
u32 LODFlag = (phaseLODs >> dlCmdNewDrawList::GetCurrDrawListLODFlagShift_UpdateThread()) & LODDrawable::LODFLAG_MASK;
|
|
const bool isShadowDrawList = DRAWLISTMGR->IsBuildingShadowDrawList();
|
|
|
|
if (LODFlag != LODDrawable::LODFLAG_NONE)
|
|
{
|
|
if (LODFlag & LODDrawable::LODFLAG_FORCE_LOD_LEVEL)
|
|
{
|
|
LODIdx = LODFlag & ~LODDrawable::LODFLAG_FORCE_LOD_LEVEL;
|
|
}
|
|
else
|
|
{
|
|
LODDrawable::CalcLODLevelAndCrossfadeForProxyLod(pDrawable, dist, LODFlag, LODIdx, crossfade, fadeDist, lastLODIdx, g_LodScale.GetGrassBatchScale());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LODDrawable::CalcLODLevelAndCrossfade(pDrawable, dist, LODIdx, crossfade, fadeDist, lastLODIdx ASSERT_ONLY(, pModelInfo), g_LodScale.GetGrassBatchScale());
|
|
}
|
|
|
|
if((LODFlag != LODDrawable::LODFLAG_NONE || isShadowDrawList) && crossfade > -1)
|
|
{
|
|
if(crossfade < 128 && LODIdx + 1 < LOD_COUNT && pDrawable->GetLodGroup().ContainsLod(LODIdx + 1))
|
|
LODIdx = LODIdx + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return LODIdx;
|
|
}
|
|
|
|
float CGrassBatch::GetShadowLODFactor()
|
|
{
|
|
return EBStatic::sShadowLodFactor;
|
|
}
|
|
|
|
#if GRASS_BATCH_CS_CULLING
|
|
const u32 EBStatic::IndirectArgParams::sIndexCountPerInstanceOffset = offsetof(EBStatic::IndirectArgParams, m_indexCountPerInstance); //Setup correct offset for index count per instance arg.
|
|
const u32 EBStatic::IndirectArgParams::sInstanceCountOffset = offsetof(EBStatic::IndirectArgParams, m_instanceCount); //Setup correct offset for instance count arg.
|
|
const u32 EBStatic::IndirectArgParams::sStartIndexLocationOffset = offsetof(EBStatic::IndirectArgParams, m_startIndexLocation);
|
|
const u32 EBStatic::IndirectArgParams::sBaseVertexLocationOffset = offsetof(EBStatic::IndirectArgParams, m_baseVertexLocation);
|
|
const u32 EBStatic::IndirectArgParams::sStartInstanceLocationOffset = offsetof(EBStatic::IndirectArgParams, m_startInstanceLocation);
|
|
|
|
EBStatic::GrassCSVars::GrassCSVars()
|
|
: m_ShaderIndex(-1)
|
|
, m_idVarAppendInstBuffer(grcevNONE)
|
|
DURANGO_ONLY(GRASS_BATCH_CS_DYNAMIC_BUFFERS_ONLY(, m_idIndirectArgs(grcevNONE)))
|
|
, m_idInstCullParams(grcevNONE)
|
|
, m_idNumClipPlanes(grcevNONE)
|
|
, m_idClipPlanes(grcevNONE)
|
|
, m_idCameraPosition(grcevNONE)
|
|
, m_idLodInstantTransition(grcevNONE)
|
|
DURANGO_ONLY(, m_idGrassSkipInstance(grcevNONE))
|
|
WIN32PC_ONLY(, m_idGrassSkipInstance(grcevNONE))
|
|
, m_idLodThresholds(grcevNONE)
|
|
, m_idCrossFadeDistance(grcevNONE)
|
|
, m_idIsShadowPass(grcevNONE)
|
|
, m_idLodFadeControlRange(grcevNONE)
|
|
DURANGO_ONLY(, m_idIndirectCountPerLod(grcevNONE))
|
|
, m_idVarInstanceBuffer(grmsgvNONE)
|
|
, m_idVarRawInstBuffer(grmsgvNONE)
|
|
, m_idVarUseCSOutputBuffer(grmsgvNONE)
|
|
, m_idAabbMin(grmsgvNONE)
|
|
, m_idAabbDelta(grmsgvNONE)
|
|
, m_idScaleRange(grmsgvNONE)
|
|
, m_IsValid(false)
|
|
{
|
|
}
|
|
|
|
void EBStatic::GrassCSVars::UpdateValidity()
|
|
{
|
|
m_IsValid = m_ShaderIndex > -1 &&
|
|
std::accumulate(m_idVarAppendInstBuffer.begin(), m_idVarAppendInstBuffer.end(), true, [](bool valid, grcEffectVar var){ return valid && var != grcevNONE; }) &&
|
|
DURANGO_ONLY(GRASS_BATCH_CS_DYNAMIC_BUFFERS_ONLY(m_idIndirectArgs != grcevNONE &&)) m_idInstCullParams != grcevNONE && m_idNumClipPlanes != grcevNONE &&
|
|
m_idClipPlanes != grcevNONE && m_idCameraPosition != grcevNONE && m_idLodInstantTransition != grcevNONE && m_idLodThresholds != grcevNONE &&
|
|
m_idCrossFadeDistance != grcevNONE && m_idIsShadowPass != grcevNONE && m_idLodFadeControlRange != grcevNONE && DURANGO_ONLY(m_idIndirectCountPerLod != grcevNONE &&)
|
|
m_idVarInstanceBuffer != grmsgvNONE && m_idVarUseCSOutputBuffer != grmsgvNONE && m_idAabbMin != grmsgvNONE && m_idAabbDelta != grmsgvNONE && m_idScaleRange != grmsgvNONE;
|
|
}
|
|
|
|
EBStatic::GrassCSBaseParams::GrassCSBaseParams()
|
|
: m_InstanceList(NULL)
|
|
, m_Vars(NULL)
|
|
, m_InstanceCount(0)
|
|
, m_InstAabbRadius(0.0f)
|
|
GRASS_BATCH_CS_DYNAMIC_BUFFERS_ONLY(, m_IndirectArgCount(0))
|
|
GRASS_BATCH_CS_DYNAMIC_BUFFERS_ONLY(, m_IndirectArgLodOffsets(0))
|
|
, m_RawInstBuffer(NULL)
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(, m_IndirectArgsBuffer(NULL))
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(, m_OffsetMap(NULL))
|
|
PIX_TAGGING_ONLY(, m_ModelIndex(-1))
|
|
, m_IsValid(false)
|
|
{
|
|
}
|
|
|
|
void EBStatic::GrassCSBaseParams::UpdateValidity()
|
|
{
|
|
m_IsValid = m_InstanceList != NULL && m_Vars && m_Vars->IsValid() && m_InstanceCount > 0 && GRASS_BATCH_CS_DYNAMIC_BUFFERS_ONLY(m_IndirectArgCount > 0 &&)
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(m_IndirectArgsBuffer != NULL && m_OffsetMap != NULL &&) m_RawInstBuffer != NULL;
|
|
}
|
|
|
|
const EBStatic::GrassCSBaseParams EBStatic::GrassCSParams::sm_InvalidBaseForDefaultConstructor;
|
|
EBStatic::GrassCSParams::GrassCSParams()
|
|
: m_Base(sm_InvalidBaseForDefaultConstructor)
|
|
, m_CurrVp(NULL)
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(, m_AppendInstBuffer(NULL))
|
|
#if GRASS_BATCH_CS_DYNAMIC_BUFFERS
|
|
, m_AppendBufferMem(NULL)
|
|
#endif //GRASS_BATCH_CS_DYNAMIC_BUFFERS
|
|
, m_phaseLODs(0)
|
|
, m_lastLODIdx(0)
|
|
, m_LODIdx(0)
|
|
, m_UseAltfadeDist(false)
|
|
, m_Active(false)
|
|
{
|
|
}
|
|
|
|
EBStatic::GrassCSParams::GrassCSParams(const GrassCSBaseParams &base)
|
|
: m_Base(base)
|
|
, m_CurrVp(NULL)
|
|
GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS_ONLY(, m_AppendInstBuffer(NULL))
|
|
#if GRASS_BATCH_CS_DYNAMIC_BUFFERS
|
|
, m_AppendBufferMem(NULL)
|
|
#endif //GRASS_BATCH_CS_DYNAMIC_BUFFERS
|
|
, m_phaseLODs(0)
|
|
, m_lastLODIdx(0)
|
|
, m_LODIdx(0)
|
|
, m_UseAltfadeDist(false)
|
|
, m_Active(false)
|
|
{
|
|
}
|
|
|
|
void CGrassBatch::GetCurrentCSParams(CSParams ¶ms) GRASS_BATCH_CS_DYNAMIC_BUFFERS_ONLY(const)
|
|
{
|
|
rage_placement_new(¶ms) EBStatic::GrassCSParams(m_CSParams); //Re-initialize params
|
|
|
|
if(DRAWLISTMGR->IsBuildingCascadeShadowDrawList())
|
|
params.m_CurrVp = NULL;
|
|
else
|
|
{
|
|
params.m_CurrVp = EBStatic::GetClippingVP();
|
|
if(params.m_CurrVp == NULL)
|
|
return;
|
|
}
|
|
|
|
//Need to find farthest point on batch...
|
|
spdAABB aabb;
|
|
GetAABB(aabb);
|
|
Vec3V camPos = VECTOR3_TO_VEC3V(camInterface::GetPos());
|
|
Vec3V farthestPointOnAABB = SelectFT(IsLessThan(camPos, aabb.GetCenter()), aabb.GetMin(), aabb.GetMax());
|
|
|
|
//Find LOD span
|
|
u32 minLod = ComputeLod(aabb.DistanceToPoint(camPos).Getf());
|
|
u32 maxLod = ComputeLod(Dist(camPos, farthestPointOnAABB).Getf());
|
|
#if __ASSERT
|
|
Vec3V closestPoint(V_NAN);
|
|
if(maxLod < minLod)
|
|
{
|
|
const Vec3V center = GetInstanceList()->m_BatchAABB.GetCenter();
|
|
const Vec3V extent = GetInstanceList()->m_BatchAABB.GetExtent();
|
|
const Vec3V v = Max(Vec3V(V_ZERO), Abs(camPos - center) - extent);
|
|
closestPoint = camPos - v;
|
|
}
|
|
#endif //_ASSERT
|
|
|
|
Assertf(maxLod >= minLod, "WARNING: maxLod is less than minLod. Please report this assert to Alan Goykhman.\n"
|
|
"Camera Pos: {%f, %f, %f}\naabb.Min: {%f, %f, %f}\naabb.Max: {%f, %f, %f}\nClosest Point: {%f, %f, %f | %f}\nFarthest Point: {%f, %f, %f | %f}\nDistance From Camera: %f\nMin|Max LOD: {%d|%d}\nEntity Name: %s",
|
|
camPos.GetXf(), camPos.GetYf(), camPos.GetZf(),
|
|
GetInstanceList()->m_BatchAABB.GetMin().GetXf(), GetInstanceList()->m_BatchAABB.GetMin().GetYf(), GetInstanceList()->m_BatchAABB.GetMin().GetZf(),
|
|
GetInstanceList()->m_BatchAABB.GetMax().GetXf(), GetInstanceList()->m_BatchAABB.GetMax().GetYf(), GetInstanceList()->m_BatchAABB.GetMax().GetZf(),
|
|
closestPoint.GetXf(), closestPoint.GetYf(), closestPoint.GetZf(), GetInstanceList()->m_BatchAABB.DistanceToPoint(camPos).Getf(),
|
|
farthestPointOnAABB.GetXf(), farthestPointOnAABB.GetYf(), farthestPointOnAABB.GetZf(), Dist(camPos, farthestPointOnAABB).Getf(),
|
|
GetDistanceToCamera(), minLod, maxLod, GetModelName());
|
|
|
|
maxLod = Max(maxLod, minLod);
|
|
u32 lodCount = 1 + (maxLod - minLod);
|
|
|
|
params.m_phaseLODs = GetPhaseLODs();
|
|
params.m_lastLODIdx = maxLod; //GetLastLODIdx();
|
|
params.m_LODIdx = minLod;
|
|
params.m_UseAltfadeDist = GetUseAltFadeDistance();
|
|
|
|
#if GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
artAssertf(m_InstanceList->m_InstanceList.GetCount() <= EBStatic::PreAllocatedAppendBufferPool::value_type::sMaxBufferCount, "WARNING! Pre-allocated append buffer size it not large enough for all instances of grass in this batch! Some instances will may not be displayed! Please report this error to the graphics team.");
|
|
if(m_InstanceList->m_InstanceList.GetCount() > EBStatic::PreAllocatedAppendBufferPool::value_type::sMaxBufferCount)
|
|
return; //Don't use CS in this case as it could cause popping. Assert will be triggered to indicate the problem.
|
|
#endif //GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
|
|
if(EBStatic::sPerfEnableComputeShader WIN32PC_ONLY(&& GRCDEVICE.SupportsFeature(COMPUTE_SHADER_50)))
|
|
{
|
|
#if GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
for(int i = 0; i < lodCount; ++i)
|
|
{
|
|
params.m_AppendInstBuffer[i] = EBStatic::sAppendBufferPool.Allocate();
|
|
Assertf(params.m_AppendInstBuffer[i], "WARNING: Failed to allocate an append buffer for grass CS. This should not happen...");
|
|
if(params.m_AppendInstBuffer[i] == NULL)
|
|
return;
|
|
}
|
|
|
|
params.m_LocalIndirectArgBuffer = EBStatic::sIndirectArgBufferPool.Allocate();
|
|
Assertf(params.m_LocalIndirectArgBuffer, "WARNING: Failed to allocate a local indirect buffer for grass CS. This should not happen...");
|
|
if(params.m_LocalIndirectArgBuffer == NULL)
|
|
return;
|
|
#endif //GRASS_BATCH_CS_PRE_ALLOCATE_BUFFERS
|
|
|
|
#if GRASS_BATCH_CS_DYNAMIC_BUFFERS
|
|
//Initialize indirect arg temp buffer so render thread can allocate later.
|
|
params.m_IndirectBufferMem.init(params.m_Base.m_IndirectArgCount ORBIS_ONLY(, sce::Gnm::kAlignmentOfIndirectArgsInBytes));
|
|
|
|
const eDrawListPageType dpType = DPT_LIFETIME_GPU_PHYSICAL;
|
|
const bool mayFail = false;
|
|
const size_t appendBufSize = GrassBatchCSInstanceData_Size * m_InstanceList->m_InstanceList.GetCount();
|
|
int appendBufAlign = GrassBatchCSInstanceData_Align; //ORBIS_ONLY(appendBufAlign = 16); //Should this be 16-byte align for PS4?
|
|
|
|
for(int i = 0; i < lodCount; ++i)
|
|
{
|
|
params.m_AppendBufferMem[i] = reinterpret_cast<void *>(gDCBuffer->AllocatePagedMemory(dpType, appendBufSize, mayFail, appendBufAlign));
|
|
if(params.m_AppendBufferMem[i] == NULL)
|
|
return;
|
|
}
|
|
Assert(params.m_AppendBufferMem[0] != NULL); //At least 1 append buffer must be created.
|
|
|
|
//Create actual buffer resources.
|
|
# if RSG_ORBIS
|
|
for(int i = 0; i < lodCount; ++i)
|
|
{
|
|
params.m_AppendDeviceBuffer[i].Initialize(params.m_AppendBufferMem[i], m_InstanceList->m_InstanceList.GetCount(), true, GrassBatchCSInstanceData_Size);
|
|
params.m_AppendDeviceBuffer[i].m_GdsCounterOffset = dlComputeShaderBatch::GetGDSOffsetForCounter();
|
|
}
|
|
# endif
|
|
#endif //GRASS_BATCH_CS_DYNAMIC_BUFFERS
|
|
|
|
params.m_Active = true;
|
|
}
|
|
}
|
|
#endif //GRASS_BATCH_CS_CULLING
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Pool functionality
|
|
//
|
|
#if __DEV
|
|
template<> void fwPool<CEntityBatch>::PoolFullCallback()
|
|
{
|
|
INSTANCE_STORE.PrintLoadedObjs();
|
|
}
|
|
|
|
template<> void fwPool<CGrassBatch>::PoolFullCallback()
|
|
{
|
|
INSTANCE_STORE.PrintLoadedObjs();
|
|
}
|
|
#endif
|
|
|
|
#if KEEP_INSTANCELIST_ASSETS_RESIDENT
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Asset loading
|
|
//
|
|
void CInstanceListAssetLoader::Init(u32 initMode)
|
|
{
|
|
if (initMode == INIT_SESSION)
|
|
{
|
|
RequestAllModels("v_proc1");
|
|
RequestAllModels("procobj"); // strictly speaking not grassbatch assets, but never mind.
|
|
}
|
|
}
|
|
|
|
void CInstanceListAssetLoader::Shutdown(u32 initMode)
|
|
{
|
|
if (initMode == SHUTDOWN_SESSION)
|
|
{
|
|
ReleaseAllModels("v_proc1");
|
|
ReleaseAllModels("procobj");
|
|
}
|
|
}
|
|
|
|
void CInstanceListAssetLoader::RequestAllModels(const char* pszItypName)
|
|
{
|
|
// load all IP veg assets up front, so they don't need to be processed by scene streaming at all
|
|
strLocalIndex itypSlot = g_MapTypesStore.FindSlot(pszItypName);
|
|
if (itypSlot.IsValid())
|
|
{
|
|
fwArchetypeManager::ForAllArchetypesInFile(itypSlot.Get(), RequestModel);
|
|
strStreamingEngine::GetLoader().LoadAllRequestedObjects();
|
|
}
|
|
}
|
|
|
|
void CInstanceListAssetLoader::ReleaseAllModels(const char* pszItypName)
|
|
{
|
|
// load all IP veg assets up front, so they don't need to be processed by scene streaming at all
|
|
strLocalIndex itypSlot = g_MapTypesStore.FindSlot(pszItypName);
|
|
if (itypSlot.IsValid())
|
|
{
|
|
fwArchetypeManager::ForAllArchetypesInFile(itypSlot.Get(), ReleaseModel);
|
|
}
|
|
}
|
|
|
|
void CInstanceListAssetLoader::RequestModel(fwArchetype* pArchetype)
|
|
{
|
|
if (pArchetype)
|
|
{
|
|
CModelInfo::RequestAssets((CBaseModelInfo*) pArchetype, STRFLAG_FORCE_LOAD|STRFLAG_PRIORITY_LOAD|STRFLAG_ZONEDASSET_REQUIRED);
|
|
}
|
|
}
|
|
|
|
void CInstanceListAssetLoader::ReleaseModel(fwArchetype* pArchetype)
|
|
{
|
|
if (pArchetype)
|
|
{
|
|
const s32 slot = pArchetype->GetStreamSlot().Get();
|
|
strStreamingModule* pModule = fwArchetypeManager::GetStreamingModule();
|
|
if (pModule)
|
|
{
|
|
pModule->ClearRequiredFlag(slot, STRFLAG_ZONEDASSET_REQUIRED);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif //KEEP_INSTANCELIST_ASSETS_RESIDENT
|