Nav apraksta
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

InstanceCuller.cs 110KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310
  1. using System;
  2. using System.Threading;
  3. using UnityEngine.Assertions;
  4. using Unity.Burst;
  5. using Unity.Burst.CompilerServices;
  6. using Unity.Mathematics;
  7. using Unity.Collections;
  8. using Unity.Collections.LowLevel.Unsafe;
  9. using Unity.Jobs;
  10. using Unity.Jobs.LowLevel.Unsafe;
  11. using UnityEngine.SceneManagement;
  12. using UnityEngine.Rendering.RenderGraphModule;
  13. #if UNITY_EDITOR
  14. using UnityEditor;
  15. using UnityEditor.Rendering;
  16. using UnityEditor.SceneManagement;
  17. #endif
  18. namespace UnityEngine.Rendering
  19. {
  20. internal struct RangeKey : IEquatable<RangeKey>
  21. {
  22. public byte layer;
  23. public uint renderingLayerMask;
  24. public MotionVectorGenerationMode motionMode;
  25. public ShadowCastingMode shadowCastingMode;
  26. public bool staticShadowCaster;
  27. public int rendererPriority;
  28. public bool supportsIndirect;
  29. public bool Equals(RangeKey other)
  30. {
  31. return
  32. layer == other.layer &&
  33. renderingLayerMask == other.renderingLayerMask &&
  34. motionMode == other.motionMode &&
  35. shadowCastingMode == other.shadowCastingMode &&
  36. staticShadowCaster == other.staticShadowCaster &&
  37. rendererPriority == other.rendererPriority &&
  38. supportsIndirect == other.supportsIndirect;
  39. }
  40. public override int GetHashCode()
  41. {
  42. int hash = 13;
  43. hash = (hash * 23) + layer;
  44. hash = (hash * 23) + (int)renderingLayerMask;
  45. hash = (hash * 23) + (int)motionMode;
  46. hash = (hash * 23) + (int)shadowCastingMode;
  47. hash = (hash * 23) + (staticShadowCaster ? 1 : 0);
  48. hash = (hash * 23) + rendererPriority;
  49. hash = (hash * 23) + (supportsIndirect ? 1 : 0);
  50. return hash;
  51. }
  52. }
  53. internal struct DrawRange
  54. {
  55. public RangeKey key;
  56. public int drawCount;
  57. public int drawOffset;
  58. }
  59. internal struct DrawKey : IEquatable<DrawKey>
  60. {
  61. public BatchMeshID meshID;
  62. public int submeshIndex;
  63. public BatchMaterialID materialID;
  64. public BatchDrawCommandFlags flags;
  65. public int transparentInstanceId; // non-zero for transparent instances, to ensure each instance has its own draw command (for sorting)
  66. public uint overridenComponents;
  67. public RangeKey range;
  68. public int lightmapIndex;
  69. public bool Equals(DrawKey other)
  70. {
  71. return
  72. meshID == other.meshID &&
  73. submeshIndex == other.submeshIndex &&
  74. materialID == other.materialID &&
  75. flags == other.flags &&
  76. transparentInstanceId == other.transparentInstanceId &&
  77. overridenComponents == other.overridenComponents &&
  78. range.Equals(other.range) &&
  79. lightmapIndex == other.lightmapIndex;
  80. }
  81. public override int GetHashCode()
  82. {
  83. int hash = 13;
  84. hash = (hash * 23) + (int)meshID.value;
  85. hash = (hash * 23) + (int)submeshIndex;
  86. hash = (hash * 23) + (int)materialID.value;
  87. hash = (hash * 23) + (int)flags;
  88. hash = (hash * 23) + transparentInstanceId;
  89. hash = (hash * 23) + range.GetHashCode();
  90. hash = (hash * 23) + (int)overridenComponents;
  91. hash = (hash * 23) + lightmapIndex;
  92. return hash;
  93. }
  94. }
  95. internal struct DrawBatch
  96. {
  97. public DrawKey key;
  98. public int instanceCount;
  99. public int instanceOffset;
  100. public MeshProceduralInfo procInfo;
  101. }
  102. internal struct DrawInstance
  103. {
  104. public DrawKey key;
  105. public int instanceIndex;
  106. }
  107. internal struct BinningConfig
  108. {
  109. public int viewCount;
  110. public bool supportsCrossFade;
  111. public bool supportsMotionCheck;
  112. public int visibilityConfigCount
  113. {
  114. get
  115. {
  116. // always bin based on flip winding state (the initial 1 bit)
  117. int bitCount = 1 + viewCount + (supportsCrossFade ? 1 : 0) + (supportsMotionCheck ? 1 : 0);
  118. return 1 << bitCount;
  119. }
  120. }
  121. }
  122. [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
  123. internal struct CullingJob : IJobParallelFor
  124. {
  125. public const int k_BatchSize = 32;
  126. const uint k_LODFadeZeroPacked = 127;
  127. const float k_LODPercentInvisible = 0.0f;
  128. const float k_LODPercentFullyVisible = 1.0f;
  129. const float k_LODPercentSpeedTree = 2.0f;
  130. const float k_SmallMeshTransitionWidth = 0.1f;
  131. enum CrossFadeType
  132. {
  133. kDisabled,
  134. kCrossFadeOut, // 1 == instance is visible in current lod, and not next - could be fading out
  135. kCrossFadeIn, // 2 == instance is visivle in next lod level, but not current - could be fading in
  136. kVisible // 3 == instance is visible in both current and next lod level - could not be impacted by fade
  137. }
  138. [ReadOnly] public BinningConfig binningConfig;
  139. [ReadOnly] public BatchCullingViewType viewType;
  140. [ReadOnly] public float3 cameraPosition;
  141. [ReadOnly] public float sqrScreenRelativeMetric;
  142. [ReadOnly] public float minScreenRelativeHeight;
  143. [ReadOnly] public bool isOrtho;
  144. [ReadOnly] public bool cullLightmappedShadowCasters;
  145. [ReadOnly] public int maxLOD;
  146. [ReadOnly] public uint cullingLayerMask;
  147. [ReadOnly] public ulong sceneCullingMask;
  148. [DeallocateOnJobCompletion] [ReadOnly] public NativeArray<FrustumPlaneCuller.PlanePacket4> frustumPlanePackets;
  149. [DeallocateOnJobCompletion] [ReadOnly] public NativeArray<FrustumPlaneCuller.SplitInfo> frustumSplitInfos;
  150. [DeallocateOnJobCompletion] [ReadOnly] public NativeArray<Plane> lightFacingFrustumPlanes;
  151. [DeallocateOnJobCompletion] [ReadOnly] public NativeArray<ReceiverSphereCuller.SplitInfo> receiverSplitInfos;
  152. public float3x3 worldToLightSpaceRotation;
  153. [ReadOnly] public CPUInstanceData.ReadOnly instanceData;
  154. [ReadOnly] public CPUSharedInstanceData.ReadOnly sharedInstanceData;
  155. [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList<LODGroupCullingData> lodGroupCullingData;
  156. [NativeDisableUnsafePtrRestriction] [ReadOnly] public IntPtr occlusionBuffer;
  157. [NativeDisableParallelForRestriction][WriteOnly] public NativeArray<byte> rendererVisibilityMasks;
  158. [NativeDisableParallelForRestriction][WriteOnly] public NativeArray<byte> rendererCrossFadeValues;
  159. // float [-1.0f... 1.0f] -> uint [0...254]
  160. static uint PackFloatToUint8(float percent)
  161. {
  162. uint packed = (uint)((1.0f + percent) * 127.0f + 0.5f);
  163. // avoid zero
  164. if (percent < 0.0f)
  165. packed = math.clamp(packed, 0, 126);
  166. else
  167. packed = math.clamp(packed, 128, 254);
  168. return packed;
  169. }
  170. unsafe float CalculateLODVisibility(int instanceIndex, int sharedInstanceIndex, InstanceFlags instanceFlags)
  171. {
  172. var lodPercent = k_LODPercentFullyVisible;
  173. var lodDataIndexAndMask = sharedInstanceData.lodGroupAndMasks[sharedInstanceIndex];
  174. if (lodDataIndexAndMask != 0xFFFFFFFF)
  175. {
  176. lodPercent = k_LODPercentInvisible;
  177. var lodIndex = lodDataIndexAndMask >> 8;
  178. var lodMask = lodDataIndexAndMask & 0xFF;
  179. Assert.IsTrue(lodMask > 0);
  180. ref var lodGroup = ref lodGroupCullingData.ElementAt((int)lodIndex);
  181. float cameraSqrDistToLODCenter = isOrtho ? sqrScreenRelativeMetric : LODGroupRenderingUtils.CalculateSqrPerspectiveDistance(lodGroup.worldSpaceReferencePoint, cameraPosition, sqrScreenRelativeMetric);
  182. // Remove lods that are beyond the max lod.
  183. uint maxLodMask = 0xffffffff << maxLOD;
  184. lodMask &= maxLodMask;
  185. // Offset to the lod preceding the first for proper cross fade calculation.
  186. int m = math.max(math.tzcnt(lodMask) - 1, maxLOD);
  187. lodMask >>= m;
  188. while (lodMask > 0)
  189. {
  190. var lodRangeSqrMin = m == maxLOD ? 0.0f : lodGroup.sqrDistances[m - 1];
  191. var lodRangeSqrMax = lodGroup.sqrDistances[m];
  192. // Camera is beyond the range of this all further lods. No need to proceed.
  193. if (cameraSqrDistToLODCenter < lodRangeSqrMin)
  194. break;
  195. // Instance is in the min/max range of this lod. Proceeding.
  196. if (cameraSqrDistToLODCenter < lodRangeSqrMax)
  197. {
  198. var type = (CrossFadeType)(lodMask & 3);
  199. // Instance is in this and/or the next lod.
  200. if (type != CrossFadeType.kDisabled)
  201. {
  202. // Instance is in both this and the next lod. No need to fade.
  203. if (type == CrossFadeType.kVisible)
  204. {
  205. lodPercent = k_LODPercentFullyVisible;
  206. }
  207. else
  208. {
  209. var distanceToLodCenter = math.sqrt(cameraSqrDistToLODCenter);
  210. var maxDist = math.sqrt(lodRangeSqrMax);
  211. // SpeedTree cross fade.
  212. if (lodGroup.percentageFlags[m])
  213. {
  214. // The fading-in instance is not visible but the fading-out is visible and it does the speed tree vertex deformation.
  215. if (type == CrossFadeType.kCrossFadeIn)
  216. {
  217. lodPercent = k_LODPercentInvisible;
  218. }
  219. else if (type == CrossFadeType.kCrossFadeOut)
  220. {
  221. var minDist = m > 0 ? math.sqrt(lodGroup.sqrDistances[m - 1]) : lodGroup.worldSpaceSize;
  222. lodPercent = k_LODPercentSpeedTree + math.max(distanceToLodCenter - minDist, 0.0f) / (maxDist - minDist);
  223. }
  224. }
  225. // Dithering cross fade.
  226. else
  227. {
  228. // If in the transition zone, both fading-in and fading-out instances are visible. Calculate the lod percent.
  229. // If not then only the fading-out instance is fully visible, and fading-in is invisible.
  230. var transitionDist = lodGroup.transitionDistances[m];
  231. var dif = maxDist - distanceToLodCenter;
  232. if (dif < transitionDist)
  233. {
  234. lodPercent = dif / transitionDist;
  235. if (type == CrossFadeType.kCrossFadeIn)
  236. lodPercent = -lodPercent;
  237. }
  238. else if (type == CrossFadeType.kCrossFadeOut)
  239. {
  240. lodPercent = k_LODPercentFullyVisible;
  241. }
  242. }
  243. }
  244. }
  245. // We found the lod and the percentage.
  246. break;
  247. }
  248. ++m;
  249. lodMask >>= 1;
  250. }
  251. }
  252. else if(viewType < BatchCullingViewType.SelectionOutline && (instanceFlags & InstanceFlags.SmallMeshCulling) != 0)
  253. {
  254. ref readonly AABB worldAABB = ref instanceData.worldAABBs.UnsafeElementAt(instanceIndex);
  255. var cameraSqrDist = isOrtho ? sqrScreenRelativeMetric : LODGroupRenderingUtils.CalculateSqrPerspectiveDistance(worldAABB.center, cameraPosition, sqrScreenRelativeMetric);
  256. var cameraDist = math.sqrt(cameraSqrDist);
  257. var aabbSize = worldAABB.extents * 2.0f;
  258. var worldSpaceSize = math.max(math.max(aabbSize.x, aabbSize.y), aabbSize.z);
  259. var maxDist = LODGroupRenderingUtils.CalculateLODDistance(minScreenRelativeHeight, worldSpaceSize);
  260. var transitionHeight = minScreenRelativeHeight + k_SmallMeshTransitionWidth * minScreenRelativeHeight;
  261. var fadeOutRange = Mathf.Max(0.0f,maxDist - LODGroupRenderingUtils.CalculateLODDistance(transitionHeight, worldSpaceSize));
  262. lodPercent = math.saturate((maxDist - cameraDist) / fadeOutRange);
  263. }
  264. return lodPercent;
  265. }
  266. private unsafe uint CalculateVisibilityMask(int instanceIndex, int sharedInstanceIndex, InstanceFlags instanceFlags)
  267. {
  268. if (cullingLayerMask == 0)
  269. return 0;
  270. if ((cullingLayerMask & (1 << sharedInstanceData.gameObjectLayers[sharedInstanceIndex])) == 0)
  271. return 0;
  272. if (cullLightmappedShadowCasters && (instanceFlags & InstanceFlags.AffectsLightmaps) != 0)
  273. return 0;
  274. #if UNITY_EDITOR
  275. if ((sceneCullingMask & instanceData.editorData.sceneCullingMasks[instanceIndex]) == 0)
  276. return 0;
  277. if(viewType == BatchCullingViewType.SelectionOutline && !instanceData.editorData.selectedBits.Get(instanceIndex))
  278. return 0;
  279. #endif
  280. // cull early for camera and shadow views based on the shadow culling mode
  281. if (viewType == BatchCullingViewType.Camera && (instanceFlags & InstanceFlags.IsShadowsOnly) != 0)
  282. return 0;
  283. if (viewType == BatchCullingViewType.Light && (instanceFlags & InstanceFlags.IsShadowsOff) != 0)
  284. return 0;
  285. ref readonly AABB worldAABB = ref instanceData.worldAABBs.UnsafeElementAt(instanceIndex);
  286. uint visibilityMask = FrustumPlaneCuller.ComputeSplitVisibilityMask(frustumPlanePackets, frustumSplitInfos, worldAABB);
  287. if (visibilityMask != 0 && receiverSplitInfos.Length > 0)
  288. visibilityMask &= ReceiverSphereCuller.ComputeSplitVisibilityMask(lightFacingFrustumPlanes, receiverSplitInfos, worldToLightSpaceRotation, worldAABB);
  289. // Perform an occlusion test on the instance bounds if we have an occlusion buffer available and the instance is still visible
  290. if (visibilityMask != 0 && occlusionBuffer != IntPtr.Zero)
  291. visibilityMask = BatchRendererGroup.OcclusionTestAABB(occlusionBuffer, worldAABB.ToBounds()) ? visibilityMask : 0;
  292. return visibilityMask;
  293. }
  294. public void Execute(int instanceIndex)
  295. {
  296. InstanceHandle instance = instanceData.instances[instanceIndex];
  297. int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance);
  298. var instanceFlags = sharedInstanceData.flags[sharedInstanceIndex].instanceFlags;
  299. var visibilityMask = CalculateVisibilityMask(instanceIndex, sharedInstanceIndex, instanceFlags);
  300. var crossFadeValue = k_LODFadeZeroPacked;
  301. if (visibilityMask != 0)
  302. {
  303. float lodPercent = CalculateLODVisibility(instanceIndex, sharedInstanceIndex, instanceFlags);
  304. if (lodPercent != k_LODPercentInvisible)
  305. {
  306. if (binningConfig.supportsMotionCheck)
  307. {
  308. bool hasMotion = instanceData.movedInPreviousFrameBits.Get(instanceIndex);
  309. visibilityMask = (visibilityMask << 1) | (hasMotion ? 1U : 0);
  310. }
  311. if (binningConfig.supportsCrossFade)
  312. {
  313. bool hasDitheringCrossFade = false;
  314. if (lodPercent != k_LODPercentFullyVisible)
  315. {
  316. bool isSpeedTreeCrossFade = lodPercent >= k_LODPercentSpeedTree;
  317. // If this is a speed tree cross fade then we provide cross fade value but we don't enable cross fade keyword.
  318. if (isSpeedTreeCrossFade)
  319. lodPercent -= k_LODPercentSpeedTree;
  320. else
  321. hasDitheringCrossFade = true;
  322. crossFadeValue = PackFloatToUint8(lodPercent);
  323. }
  324. visibilityMask = (visibilityMask << 1) | (hasDitheringCrossFade ? 1U : 0);
  325. }
  326. }
  327. else
  328. {
  329. visibilityMask = 0;
  330. }
  331. }
  332. rendererVisibilityMasks[instance.index] = (byte)visibilityMask;
  333. rendererCrossFadeValues[instance.index] = (byte)crossFadeValue;
  334. }
  335. }
  336. [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
  337. internal unsafe struct AllocateBinsPerBatch : IJobParallelFor
  338. {
  339. [ReadOnly] public BinningConfig binningConfig;
  340. [ReadOnly] public NativeList<DrawBatch> drawBatches;
  341. [ReadOnly] public NativeArray<int> drawInstanceIndices;
  342. [ReadOnly] public CPUInstanceData.ReadOnly instanceData;
  343. [ReadOnly] public NativeArray<byte> rendererVisibilityMasks;
  344. [NativeDisableContainerSafetyRestriction, NoAlias] [WriteOnly] public NativeArray<int> batchBinAllocOffsets;
  345. [NativeDisableContainerSafetyRestriction, NoAlias] [WriteOnly] public NativeArray<int> batchBinCounts;
  346. [NativeDisableContainerSafetyRestriction, NoAlias] [DeallocateOnJobCompletion] public NativeArray<int> binAllocCounter;
  347. [NativeDisableContainerSafetyRestriction, NoAlias] [WriteOnly] public NativeArray<short> binConfigIndices;
  348. [NativeDisableContainerSafetyRestriction, NoAlias] [WriteOnly] public NativeArray<int> binVisibleInstanceCounts;
  349. [ReadOnly] public int debugCounterIndexBase;
  350. [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray<int> splitDebugCounters;
  351. bool IsInstanceFlipped(int rendererIndex)
  352. {
  353. InstanceHandle instance = InstanceHandle.FromInt(rendererIndex);
  354. int instanceIndex = instanceData.InstanceToIndex(instance);
  355. return instanceData.localToWorldIsFlippedBits.Get(instanceIndex);
  356. }
  357. unsafe public void Execute(int batchIndex)
  358. {
  359. // figure out how many combinations of views/features we need to partition by
  360. int configCount = binningConfig.visibilityConfigCount;
  361. // allocate space to keep track of the number of instances per config
  362. var visibleCountPerConfig = stackalloc int[configCount];
  363. for (int i = 0; i < configCount; ++i)
  364. visibleCountPerConfig[i] = 0;
  365. // and space to keep track of which configs have any instances
  366. int configMaskCount = (configCount + 63)/64;
  367. var configUsedMasks = stackalloc UInt64[configMaskCount];
  368. for (int i = 0; i < configMaskCount; ++i)
  369. configUsedMasks[i] = 0;
  370. // loop over all instances within this batch
  371. var drawBatch = drawBatches[batchIndex];
  372. var instanceCount = drawBatch.instanceCount;
  373. var instanceOffset = drawBatch.instanceOffset;
  374. for (int i = 0; i < instanceCount; ++i)
  375. {
  376. var rendererIndex = drawInstanceIndices[instanceOffset + i];
  377. bool isFlipped = IsInstanceFlipped(rendererIndex);
  378. int visibilityMask = (int)rendererVisibilityMasks[rendererIndex];
  379. if (visibilityMask == 0)
  380. continue;
  381. int configIndex = (int)(visibilityMask << 1) | (isFlipped ? 1 : 0);
  382. Assert.IsTrue(configIndex < configCount);
  383. visibleCountPerConfig[configIndex]++;
  384. configUsedMasks[configIndex >> 6] |= 1ul << (configIndex & 0x3f);
  385. }
  386. // allocate and store the non-empty configs as bins
  387. int binCount = 0;
  388. for (int i = 0; i < configMaskCount; ++i)
  389. binCount += math.countbits(configUsedMasks[i]);
  390. int allocOffsetStart = 0;
  391. if (binCount > 0)
  392. {
  393. var drawCommandCountPerView = stackalloc int[binningConfig.viewCount];
  394. var visibleCountPerView = stackalloc int[binningConfig.viewCount];
  395. for (int i = 0; i < binningConfig.viewCount; ++i)
  396. {
  397. drawCommandCountPerView[i] = 0;
  398. visibleCountPerView[i] = 0;
  399. }
  400. bool countVisibilityStats = (debugCounterIndexBase >= 0);
  401. int shiftForVisibilityMask = 1 + (binningConfig.supportsMotionCheck ? 1 : 0) + (binningConfig.supportsCrossFade ? 1 : 0);
  402. int *allocCounter = (int *)binAllocCounter.GetUnsafePtr<int>();
  403. int allocOffsetEnd = Interlocked.Add(ref UnsafeUtility.AsRef<int>(allocCounter), binCount);
  404. allocOffsetStart = allocOffsetEnd - binCount;
  405. int allocOffset = allocOffsetStart;
  406. for (int i = 0; i < configMaskCount; ++i)
  407. {
  408. UInt64 configRemainMask = configUsedMasks[i];
  409. while (configRemainMask != 0)
  410. {
  411. var bitPos = math.tzcnt(configRemainMask);
  412. configRemainMask ^= 1ul << bitPos;
  413. int configIndex = 64*i + bitPos;
  414. int visibleCount = visibleCountPerConfig[configIndex];
  415. Assert.IsTrue(visibleCount > 0);
  416. binConfigIndices[allocOffset] = (short)configIndex;
  417. binVisibleInstanceCounts[allocOffset] = visibleCount;
  418. allocOffset++;
  419. int visibilityMask = countVisibilityStats ? (configIndex >> shiftForVisibilityMask) : 0;
  420. while (visibilityMask != 0)
  421. {
  422. var viewIndex = math.tzcnt(visibilityMask);
  423. visibilityMask ^= 1 << viewIndex;
  424. drawCommandCountPerView[viewIndex] += 1;
  425. visibleCountPerView[viewIndex] += visibleCount;
  426. }
  427. }
  428. }
  429. Assert.IsTrue(allocOffset == allocOffsetEnd);
  430. if (countVisibilityStats)
  431. {
  432. for (int viewIndex = 0; viewIndex < binningConfig.viewCount; ++viewIndex)
  433. {
  434. int* counterPtr = (int*)splitDebugCounters.GetUnsafePtr() + (debugCounterIndexBase + viewIndex) * (int)InstanceCullerSplitDebugCounter.Count;
  435. int drawCommandCount = drawCommandCountPerView[viewIndex];
  436. if (drawCommandCount > 0)
  437. Interlocked.Add(ref UnsafeUtility.AsRef<int>(counterPtr + (int)InstanceCullerSplitDebugCounter.DrawCommands), drawCommandCount);
  438. int visibleCount = visibleCountPerView[viewIndex];
  439. if (visibleCount > 0)
  440. Interlocked.Add(ref UnsafeUtility.AsRef<int>(counterPtr + (int)InstanceCullerSplitDebugCounter.VisibleInstances), visibleCount);
  441. }
  442. }
  443. }
  444. batchBinAllocOffsets[batchIndex] = allocOffsetStart;
  445. batchBinCounts[batchIndex] = binCount;
  446. }
  447. }
  448. [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
  449. internal unsafe struct PrefixSumDrawsAndInstances : IJob
  450. {
  451. [ReadOnly] public NativeList<DrawRange> drawRanges;
  452. [ReadOnly] public NativeArray<int> drawBatchIndices;
  453. [ReadOnly] public NativeArray<int> batchBinAllocOffsets;
  454. [ReadOnly] public NativeArray<int> batchBinCounts;
  455. [ReadOnly] public NativeArray<int> binVisibleInstanceCounts;
  456. [NativeDisableContainerSafetyRestriction, NoAlias] [WriteOnly] public NativeArray<int> batchDrawCommandOffsets;
  457. [NativeDisableContainerSafetyRestriction, NoAlias] [WriteOnly] public NativeArray<int> binVisibleInstanceOffsets;
  458. [NativeDisableUnsafePtrRestriction] public NativeArray<BatchCullingOutputDrawCommands> cullingOutput;
  459. [ReadOnly] public IndirectBufferLimits indirectBufferLimits;
  460. [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray<IndirectBufferAllocInfo> indirectBufferAllocInfo;
  461. [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray<int> indirectAllocationCounters;
  462. unsafe public void Execute()
  463. {
  464. BatchCullingOutputDrawCommands output = cullingOutput[0];
  465. bool allowIndirect = indirectBufferLimits.maxInstanceCount > 0;
  466. int outRangeIndex;
  467. int outDirectCommandIndex;
  468. int outDirectVisibleInstanceIndex;
  469. int outIndirectCommandIndex;
  470. int outIndirectVisibleInstanceIndex;
  471. for (;;)
  472. {
  473. // reset counters
  474. outRangeIndex = 0;
  475. outDirectCommandIndex = 0;
  476. outDirectVisibleInstanceIndex = 0;
  477. outIndirectCommandIndex = 0;
  478. outIndirectVisibleInstanceIndex = 0;
  479. for (int rangeIndex = 0; rangeIndex < drawRanges.Length; ++rangeIndex)
  480. {
  481. var drawRangeInfo = drawRanges[rangeIndex];
  482. bool isIndirect = allowIndirect && drawRangeInfo.key.supportsIndirect;
  483. int rangeDrawCommandCount = 0;
  484. int rangeDrawCommandOffset = isIndirect ? outIndirectCommandIndex : outDirectCommandIndex;
  485. for (int drawIndexInRange = 0; drawIndexInRange < drawRangeInfo.drawCount; ++drawIndexInRange)
  486. {
  487. var batchIndex = drawBatchIndices[drawRangeInfo.drawOffset + drawIndexInRange];
  488. var binAllocOffset = batchBinAllocOffsets[batchIndex];
  489. var binCount = batchBinCounts[batchIndex];
  490. if (isIndirect)
  491. {
  492. batchDrawCommandOffsets[batchIndex] = outIndirectCommandIndex;
  493. outIndirectCommandIndex += binCount;
  494. }
  495. else
  496. {
  497. batchDrawCommandOffsets[batchIndex] = outDirectCommandIndex;
  498. outDirectCommandIndex += binCount;
  499. }
  500. rangeDrawCommandCount += binCount;
  501. for (int binIndexInBatch = 0; binIndexInBatch < binCount; ++binIndexInBatch)
  502. {
  503. var binIndex = binAllocOffset + binIndexInBatch;
  504. if (isIndirect)
  505. {
  506. binVisibleInstanceOffsets[binIndex] = outIndirectVisibleInstanceIndex;
  507. outIndirectVisibleInstanceIndex += binVisibleInstanceCounts[binIndex];
  508. }
  509. else
  510. {
  511. binVisibleInstanceOffsets[binIndex] = outDirectVisibleInstanceIndex;
  512. outDirectVisibleInstanceIndex += binVisibleInstanceCounts[binIndex];
  513. }
  514. }
  515. }
  516. if (rangeDrawCommandCount != 0)
  517. {
  518. #if DEBUG
  519. if (outRangeIndex >= output.drawRangeCount)
  520. throw new Exception("Exceeding draw range count");
  521. #endif
  522. var rangeKey = drawRangeInfo.key;
  523. output.drawRanges[outRangeIndex] = new BatchDrawRange
  524. {
  525. drawCommandsBegin = (uint)rangeDrawCommandOffset,
  526. drawCommandsCount = (uint)rangeDrawCommandCount,
  527. drawCommandsType = isIndirect ? BatchDrawCommandType.Indirect : BatchDrawCommandType.Direct,
  528. filterSettings = new BatchFilterSettings
  529. {
  530. renderingLayerMask = rangeKey.renderingLayerMask,
  531. rendererPriority = rangeKey.rendererPriority,
  532. layer = rangeKey.layer,
  533. batchLayer = isIndirect ? BatchLayer.InstanceCullingIndirect : BatchLayer.InstanceCullingDirect,
  534. motionMode = rangeKey.motionMode,
  535. shadowCastingMode = rangeKey.shadowCastingMode,
  536. receiveShadows = true,
  537. staticShadowCaster = rangeKey.staticShadowCaster,
  538. allDepthSorted = false,
  539. }
  540. };
  541. outRangeIndex++;
  542. }
  543. }
  544. output.drawRangeCount = outRangeIndex; // trim to the number of written ranges
  545. // try to allocate buffer space for indirect
  546. bool isValid = true;
  547. if (allowIndirect)
  548. {
  549. int* allocCounters = (int*)indirectAllocationCounters.GetUnsafePtr<int>();
  550. var allocInfo = new IndirectBufferAllocInfo();
  551. allocInfo.drawCount = outIndirectCommandIndex;
  552. allocInfo.instanceCount = outIndirectVisibleInstanceIndex;
  553. int drawAllocCount = allocInfo.drawCount + IndirectBufferContextStorage.kExtraDrawAllocationCount;
  554. int drawAllocEnd = Interlocked.Add(ref UnsafeUtility.AsRef<int>(allocCounters + (int)IndirectAllocator.NextDrawIndex), drawAllocCount);
  555. allocInfo.drawAllocIndex = drawAllocEnd - drawAllocCount;
  556. int instanceAllocEnd = Interlocked.Add(ref UnsafeUtility.AsRef<int>(allocCounters + (int)IndirectAllocator.NextInstanceIndex), allocInfo.instanceCount);
  557. allocInfo.instanceAllocIndex = instanceAllocEnd - allocInfo.instanceCount;
  558. if (!allocInfo.IsWithinLimits(indirectBufferLimits))
  559. {
  560. allocInfo = new IndirectBufferAllocInfo();
  561. isValid = false;
  562. }
  563. indirectBufferAllocInfo[0] = allocInfo;
  564. }
  565. if (isValid)
  566. break;
  567. // out of indirect memory, reset counters and try again without indirect
  568. //Debug.Log("Out of indirect buffer space: falling back to direct draws for this frame!");
  569. allowIndirect = false;
  570. }
  571. if (outDirectCommandIndex != 0)
  572. {
  573. output.drawCommandCount = outDirectCommandIndex;
  574. output.drawCommands = MemoryUtilities.Malloc<BatchDrawCommand>(outDirectCommandIndex, Allocator.TempJob);
  575. output.visibleInstanceCount = outDirectVisibleInstanceIndex;
  576. output.visibleInstances = MemoryUtilities.Malloc<int>(outDirectVisibleInstanceIndex, Allocator.TempJob);
  577. }
  578. if (outIndirectCommandIndex != 0)
  579. {
  580. output.indirectDrawCommandCount = outIndirectCommandIndex;
  581. output.indirectDrawCommands = MemoryUtilities.Malloc<BatchDrawCommandIndirect>(outIndirectCommandIndex, Allocator.TempJob);
  582. }
  583. int totalCommandCount = outDirectCommandIndex + outIndirectCommandIndex;
  584. output.instanceSortingPositions = MemoryUtilities.Malloc<float>(3 * totalCommandCount, Allocator.TempJob);
  585. cullingOutput[0] = output;
  586. }
  587. }
  588. [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
  589. internal unsafe struct DrawCommandOutputPerBatch : IJobParallelFor
  590. {
  591. [ReadOnly] public BinningConfig binningConfig;
  592. [ReadOnly] public NativeParallelHashMap<uint, BatchID> batchIDs;
  593. [ReadOnly] public GPUInstanceDataBuffer.ReadOnly instanceDataBuffer;
  594. [ReadOnly] public NativeList<DrawBatch> drawBatches;
  595. [ReadOnly] public NativeArray<int> drawInstanceIndices;
  596. [ReadOnly] public CPUInstanceData.ReadOnly instanceData;
  597. [ReadOnly] public NativeArray<byte> rendererVisibilityMasks;
  598. [ReadOnly] public NativeArray<byte> rendererCrossFadeValues;
  599. [ReadOnly] [DeallocateOnJobCompletion] public NativeArray<int> batchBinAllocOffsets;
  600. [ReadOnly] [DeallocateOnJobCompletion] public NativeArray<int> batchBinCounts;
  601. [ReadOnly] [DeallocateOnJobCompletion] public NativeArray<int> batchDrawCommandOffsets;
  602. [ReadOnly] [DeallocateOnJobCompletion] public NativeArray<short> binConfigIndices;
  603. [ReadOnly] [DeallocateOnJobCompletion] public NativeArray<int> binVisibleInstanceOffsets;
  604. [ReadOnly] [DeallocateOnJobCompletion] public NativeArray<int> binVisibleInstanceCounts;
  605. [ReadOnly] public NativeArray<BatchCullingOutputDrawCommands> cullingOutput;
  606. [ReadOnly] public IndirectBufferLimits indirectBufferLimits;
  607. [ReadOnly] public GraphicsBufferHandle visibleInstancesBufferHandle;
  608. [ReadOnly] public GraphicsBufferHandle indirectArgsBufferHandle;
  609. [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray<IndirectBufferAllocInfo> indirectBufferAllocInfo;
  610. [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray<IndirectDrawInfo> indirectDrawInfoGlobalArray;
  611. [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray<IndirectInstanceInfo> indirectInstanceInfoGlobalArray;
  612. unsafe int EncodeGPUInstanceIndexAndCrossFade(int rendererIndex, bool negateCrossFade)
  613. {
  614. var gpuInstanceIndex = instanceDataBuffer.CPUInstanceToGPUInstance(InstanceHandle.FromInt(rendererIndex));
  615. int crossFadeValue = rendererCrossFadeValues[rendererIndex];
  616. crossFadeValue -= 127;
  617. if (negateCrossFade)
  618. crossFadeValue = -crossFadeValue;
  619. gpuInstanceIndex.index |= crossFadeValue << 24;
  620. return gpuInstanceIndex.index;
  621. }
  622. bool IsInstanceFlipped(int rendererIndex)
  623. {
  624. InstanceHandle instance = InstanceHandle.FromInt(rendererIndex);
  625. int instanceIndex = instanceData.InstanceToIndex(instance);
  626. return instanceData.localToWorldIsFlippedBits.Get(instanceIndex);
  627. }
  628. unsafe public void Execute(int batchIndex)
  629. {
  630. DrawBatch drawBatch = drawBatches[batchIndex];
  631. var binCount = batchBinCounts[batchIndex];
  632. if (binCount == 0)
  633. return;
  634. BatchCullingOutputDrawCommands output = cullingOutput[0];
  635. IndirectBufferAllocInfo indirectAllocInfo = new IndirectBufferAllocInfo();
  636. if (indirectBufferLimits.maxDrawCount > 0)
  637. indirectAllocInfo = indirectBufferAllocInfo[0];
  638. bool allowIndirect = !indirectAllocInfo.IsEmpty();
  639. bool isIndirect = allowIndirect && drawBatch.key.range.supportsIndirect;
  640. // figure out how many combinations of views/features we need to partition by
  641. int configCount = binningConfig.visibilityConfigCount;
  642. // allocate storage for the instance offsets, set to zero
  643. var instanceOffsetPerConfig = stackalloc int[configCount];
  644. for (int i = 0; i < configCount; ++i)
  645. instanceOffsetPerConfig[i] = 0;
  646. // allocate storage to be able to look up the draw index per instance (by config)
  647. var drawCommandOffsetPerConfig = stackalloc int[configCount];
  648. // write the draw commands, scatter the allocated offsets to our storage
  649. // TODO: fast path when binCount == 1
  650. var batchBinAllocOffset = batchBinAllocOffsets[batchIndex];
  651. var batchDrawCommandOffset = batchDrawCommandOffsets[batchIndex];
  652. var lastBinInstanceOffset = 0;
  653. bool rangeSupportsMotion = (drawBatch.key.range.motionMode == MotionVectorGenerationMode.Object ||
  654. drawBatch.key.range.motionMode == MotionVectorGenerationMode.ForceNoMotion);
  655. for (int binIndexInBatch = 0; binIndexInBatch < binCount; ++binIndexInBatch)
  656. {
  657. var binIndex = batchBinAllocOffset + binIndexInBatch;
  658. var visibleInstanceOffset = binVisibleInstanceOffsets[binIndex];
  659. var visibleInstanceCount = binVisibleInstanceCounts[binIndex];
  660. lastBinInstanceOffset = visibleInstanceOffset;
  661. // scatter to local storage for the per-instance loop below
  662. var configIndex = binConfigIndices[binIndex];
  663. instanceOffsetPerConfig[configIndex] = visibleInstanceOffset;
  664. // get the write index for the draw command
  665. var drawCommandOffset = batchDrawCommandOffset + binIndexInBatch;
  666. drawCommandOffsetPerConfig[configIndex] = drawCommandOffset;
  667. var drawFlags = drawBatch.key.flags;
  668. bool isFlipped = ((configIndex & 1) != 0);
  669. if (isFlipped)
  670. drawFlags |= BatchDrawCommandFlags.FlipWinding;
  671. int visibilityMask = configIndex >> 1;
  672. if (binningConfig.supportsCrossFade)
  673. {
  674. if ((visibilityMask & 1) != 0)
  675. drawFlags |= BatchDrawCommandFlags.LODCrossFadeKeyword;
  676. visibilityMask >>= 1;
  677. }
  678. if (binningConfig.supportsMotionCheck)
  679. {
  680. if ((visibilityMask & 1) != 0 && rangeSupportsMotion)
  681. drawFlags |= BatchDrawCommandFlags.HasMotion;
  682. visibilityMask >>= 1;
  683. }
  684. Assert.IsTrue(visibilityMask != 0);
  685. var sortingPosition = 0;
  686. if ((drawFlags & BatchDrawCommandFlags.HasSortingPosition) != 0)
  687. {
  688. int globalCommandOffset = drawCommandOffset;
  689. if (isIndirect)
  690. globalCommandOffset += output.drawCommandCount; // skip over direct commands
  691. sortingPosition = 3 * globalCommandOffset;
  692. }
  693. #if DEBUG
  694. if (!batchIDs.ContainsKey(drawBatch.key.overridenComponents))
  695. throw new Exception("Draw command created with an invalid BatchID");
  696. #endif
  697. if (isIndirect)
  698. {
  699. #if DEBUG
  700. if (drawCommandOffset >= output.indirectDrawCommandCount)
  701. throw new Exception("Exceeding draw command count");
  702. #endif
  703. int instanceInfoGlobalIndex = indirectAllocInfo.instanceAllocIndex + visibleInstanceOffset;
  704. int drawInfoGlobalIndex = indirectAllocInfo.drawAllocIndex + drawCommandOffset;
  705. indirectDrawInfoGlobalArray[drawInfoGlobalIndex] = new IndirectDrawInfo
  706. {
  707. indexCount = drawBatch.procInfo.indexCount,
  708. firstIndex = drawBatch.procInfo.firstIndex,
  709. baseVertex = drawBatch.procInfo.baseVertex,
  710. firstInstanceGlobalIndex = (uint)instanceInfoGlobalIndex,
  711. maxInstanceCount = (uint)visibleInstanceCount,
  712. };
  713. output.indirectDrawCommands[drawCommandOffset] = new BatchDrawCommandIndirect
  714. {
  715. flags = drawFlags,
  716. visibleOffset = (uint)instanceInfoGlobalIndex,
  717. batchID = batchIDs[drawBatch.key.overridenComponents],
  718. materialID = drawBatch.key.materialID,
  719. splitVisibilityMask = (ushort)visibilityMask,
  720. lightmapIndex = (ushort)drawBatch.key.lightmapIndex,
  721. sortingPosition = sortingPosition,
  722. meshID = drawBatch.key.meshID,
  723. topology = drawBatch.procInfo.topology,
  724. visibleInstancesBufferHandle = visibleInstancesBufferHandle,
  725. indirectArgsBufferHandle = indirectArgsBufferHandle,
  726. indirectArgsBufferOffset = (uint)(drawInfoGlobalIndex * GraphicsBuffer.IndirectDrawIndexedArgs.size),
  727. };
  728. }
  729. else
  730. {
  731. #if DEBUG
  732. if (drawCommandOffset >= output.drawCommandCount)
  733. throw new Exception("Exceeding draw command count");
  734. #endif
  735. output.drawCommands[drawCommandOffset] = new BatchDrawCommand
  736. {
  737. flags = drawFlags,
  738. visibleOffset = (uint)visibleInstanceOffset,
  739. visibleCount = (uint)visibleInstanceCount,
  740. batchID = batchIDs[drawBatch.key.overridenComponents],
  741. materialID = drawBatch.key.materialID,
  742. splitVisibilityMask = (ushort)visibilityMask,
  743. lightmapIndex = (ushort)drawBatch.key.lightmapIndex,
  744. sortingPosition = sortingPosition,
  745. meshID = drawBatch.key.meshID,
  746. submeshIndex = (ushort)drawBatch.key.submeshIndex,
  747. };
  748. }
  749. }
  750. // write the visible instances
  751. var instanceOffset = drawBatch.instanceOffset;
  752. var instanceCount = drawBatch.instanceCount;
  753. var lastRendererIndex = 0;
  754. if (binCount > 1)
  755. {
  756. for (int i = 0; i < instanceCount; ++i)
  757. {
  758. var rendererIndex = drawInstanceIndices[instanceOffset + i];
  759. bool isFlipped = IsInstanceFlipped(rendererIndex);
  760. int visibilityMask = (int)rendererVisibilityMasks[rendererIndex];
  761. if (visibilityMask == 0)
  762. continue;
  763. lastRendererIndex = rendererIndex;
  764. // add to the instance list for this bin
  765. int configIndex = (int)(visibilityMask << 1) | (isFlipped ? 1 : 0);
  766. Assert.IsTrue(configIndex < binningConfig.visibilityConfigCount);
  767. var visibleInstanceOffset = instanceOffsetPerConfig[configIndex];
  768. instanceOffsetPerConfig[configIndex]++;
  769. if (isIndirect)
  770. {
  771. #if DEBUG
  772. if (visibleInstanceOffset >= indirectAllocInfo.instanceCount)
  773. throw new Exception("Exceeding visible instance count");
  774. #endif
  775. // remove extra bits so that the visibility mask is just the view mask
  776. if (binningConfig.supportsCrossFade)
  777. visibilityMask >>= 1;
  778. if (binningConfig.supportsMotionCheck)
  779. visibilityMask >>= 1;
  780. indirectInstanceInfoGlobalArray[indirectAllocInfo.instanceAllocIndex + visibleInstanceOffset] = new IndirectInstanceInfo
  781. {
  782. drawOffsetAndSplitMask = (drawCommandOffsetPerConfig[configIndex] << 8) | visibilityMask,
  783. instanceIndexAndCrossFade = EncodeGPUInstanceIndexAndCrossFade(rendererIndex, false),
  784. };
  785. }
  786. else
  787. {
  788. #if DEBUG
  789. if (visibleInstanceOffset >= output.visibleInstanceCount)
  790. throw new Exception("Exceeding visible instance count");
  791. #endif
  792. output.visibleInstances[visibleInstanceOffset] = EncodeGPUInstanceIndexAndCrossFade(rendererIndex, false);
  793. }
  794. }
  795. }
  796. else
  797. {
  798. int visibleInstanceOffset = lastBinInstanceOffset;
  799. for (int i = 0; i < instanceCount; ++i)
  800. {
  801. var rendererIndex = drawInstanceIndices[instanceOffset + i];
  802. int visibilityMask = (int)rendererVisibilityMasks[rendererIndex];
  803. bool isVisible = (visibilityMask != 0);
  804. if (!isVisible)
  805. continue;
  806. lastRendererIndex = rendererIndex;
  807. if (isIndirect)
  808. {
  809. // remove extra bits so that the visibility mask is just the view mask
  810. if (binningConfig.supportsCrossFade)
  811. visibilityMask >>= 1;
  812. if (binningConfig.supportsMotionCheck)
  813. visibilityMask >>= 1;
  814. indirectInstanceInfoGlobalArray[indirectAllocInfo.instanceAllocIndex + visibleInstanceOffset] = new IndirectInstanceInfo
  815. {
  816. drawOffsetAndSplitMask = (batchDrawCommandOffset << 8) | visibilityMask,
  817. instanceIndexAndCrossFade = EncodeGPUInstanceIndexAndCrossFade(rendererIndex, false),
  818. };
  819. }
  820. else
  821. {
  822. output.visibleInstances[visibleInstanceOffset] = EncodeGPUInstanceIndexAndCrossFade(rendererIndex, false);
  823. }
  824. visibleInstanceOffset++;
  825. }
  826. }
  827. // use the first instance position of each batch as the sorting position if necessary
  828. if ((drawBatch.key.flags & BatchDrawCommandFlags.HasSortingPosition) != 0)
  829. {
  830. InstanceHandle instance = InstanceHandle.FromInt(lastRendererIndex & 0xffffff);
  831. int instanceIndex = instanceData.InstanceToIndex(instance);
  832. ref readonly AABB worldAABB = ref instanceData.worldAABBs.UnsafeElementAt(instanceIndex);
  833. float3 position = worldAABB.center;
  834. int globalCommandOffset = batchDrawCommandOffset;
  835. if (isIndirect)
  836. globalCommandOffset += output.drawCommandCount; // skip over direct commands
  837. int sortingPosition = 3 * globalCommandOffset;
  838. output.instanceSortingPositions[sortingPosition + 0] = position.x;
  839. output.instanceSortingPositions[sortingPosition + 1] = position.y;
  840. output.instanceSortingPositions[sortingPosition + 2] = position.z;
  841. }
  842. }
  843. }
  844. [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
  845. internal unsafe struct CompactVisibilityMasksJob : IJobParallelForBatch
  846. {
  847. public const int k_BatchSize = 64;
  848. [ReadOnly] public NativeArray<byte> rendererVisibilityMasks;
  849. [NativeDisableContainerSafetyRestriction, NoAlias] public ParallelBitArray compactedVisibilityMasks;
  850. unsafe public void Execute(int startIndex, int count)
  851. {
  852. ulong chunkBits = 0;
  853. for(int i = 0; i < count; ++i)
  854. {
  855. var visibilityMask = rendererVisibilityMasks[startIndex + i];
  856. if(visibilityMask != 0)
  857. chunkBits |= (1ul << i);
  858. }
  859. var chunkIndex = startIndex / k_BatchSize;
  860. compactedVisibilityMasks.InterlockedOrChunk(chunkIndex, chunkBits);
  861. }
  862. }
  863. #if UNITY_EDITOR
  864. internal enum FilteringJobMode
  865. {
  866. Filtering,
  867. Picking
  868. }
  869. [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
  870. internal unsafe struct DrawCommandOutputFiltering : IJob
  871. {
  872. [ReadOnly] public NativeParallelHashMap<uint, BatchID> batchIDs;
  873. [ReadOnly] public int viewID;
  874. [ReadOnly] public GPUInstanceDataBuffer.ReadOnly instanceDataBuffer;
  875. [ReadOnly] public NativeArray<byte> rendererVisibilityMasks;
  876. [ReadOnly] public NativeArray<byte> rendererCrossFadeValues;
  877. [ReadOnly] public CPUInstanceData.ReadOnly instanceData;
  878. [ReadOnly] public CPUSharedInstanceData.ReadOnly sharedInstanceData;
  879. [ReadOnly] public NativeArray<int> drawInstanceIndices;
  880. [ReadOnly] public NativeList<DrawBatch> drawBatches;
  881. [ReadOnly] public NativeList<DrawRange> drawRanges;
  882. [ReadOnly] public NativeArray<int> drawBatchIndices;
  883. [ReadOnly] public NativeArray<bool> filteringResults;
  884. [ReadOnly] public NativeArray<int> excludedRenderers;
  885. [ReadOnly] public FilteringJobMode mode;
  886. [NativeDisableUnsafePtrRestriction] public NativeArray<BatchCullingOutputDrawCommands> cullingOutput;
  887. #if DEBUG
  888. [IgnoreWarning(1370)] //Ignore throwing exception warning.
  889. #endif
  890. public void Execute()
  891. {
  892. BatchCullingOutputDrawCommands output = cullingOutput[0];
  893. int maxVisibleInstanceCount = 0;
  894. for (int i = 0; i < drawInstanceIndices.Length; ++i)
  895. {
  896. var rendererIndex = drawInstanceIndices[i];
  897. if (rendererVisibilityMasks[rendererIndex] != 0)
  898. ++maxVisibleInstanceCount;
  899. }
  900. output.visibleInstanceCount = maxVisibleInstanceCount;
  901. output.visibleInstances = MemoryUtilities.Malloc<int>(output.visibleInstanceCount, Allocator.TempJob);
  902. output.drawCommandCount = output.visibleInstanceCount; // for picking/filtering, 1 draw command per instance!
  903. output.drawCommands = MemoryUtilities.Malloc<BatchDrawCommand>(output.drawCommandCount, Allocator.TempJob);
  904. output.drawCommandPickingInstanceIDs = MemoryUtilities.Malloc<int>(output.drawCommandCount, Allocator.TempJob);
  905. int outRangeIndex = 0;
  906. int outCommandIndex = 0;
  907. int outVisibleInstanceIndex = 0;
  908. for (int rangeIndex = 0; rangeIndex < drawRanges.Length; ++rangeIndex)
  909. {
  910. int rangeDrawCommandOffset = outCommandIndex;
  911. var drawRangeInfo = drawRanges[rangeIndex];
  912. for (int drawIndexInRange = 0; drawIndexInRange < drawRangeInfo.drawCount; ++drawIndexInRange)
  913. {
  914. var batchIndex = drawBatchIndices[drawRangeInfo.drawOffset + drawIndexInRange];
  915. DrawBatch drawBatch = drawBatches[batchIndex];
  916. var instanceOffset = drawBatch.instanceOffset;
  917. var instanceCount = drawBatch.instanceCount;
  918. // Output visible instances to the array
  919. for (int i = 0; i < instanceCount; ++i)
  920. {
  921. var rendererIndex = drawInstanceIndices[instanceOffset + i];
  922. var visibilityMask = rendererVisibilityMasks[rendererIndex];
  923. if (visibilityMask == 0)
  924. continue;
  925. InstanceHandle instance = InstanceHandle.FromInt(rendererIndex);
  926. int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance);
  927. if (mode == FilteringJobMode.Filtering && filteringResults.IsCreated && (sharedInstanceIndex >= filteringResults.Length || !filteringResults[sharedInstanceIndex]))
  928. continue;
  929. var rendererID = sharedInstanceData.rendererGroupIDs[sharedInstanceIndex];
  930. if (mode == FilteringJobMode.Picking && excludedRenderers.IsCreated && excludedRenderers.Contains(rendererID))
  931. continue;
  932. #if DEBUG
  933. if (outVisibleInstanceIndex >= output.visibleInstanceCount)
  934. throw new Exception("Exceeding visible instance count");
  935. if (outCommandIndex >= output.drawCommandCount)
  936. throw new Exception("Exceeding draw command count");
  937. if (!batchIDs.ContainsKey(drawBatch.key.overridenComponents))
  938. throw new Exception("Draw command created with an invalid BatchID");
  939. #endif
  940. output.visibleInstances[outVisibleInstanceIndex] = instanceDataBuffer.CPUInstanceToGPUInstance(instance).index;
  941. output.drawCommandPickingInstanceIDs[outCommandIndex] = rendererID;
  942. output.drawCommands[outCommandIndex] = new BatchDrawCommand
  943. {
  944. flags = BatchDrawCommandFlags.None,
  945. visibleOffset = (uint)outVisibleInstanceIndex,
  946. visibleCount = (uint)1,
  947. batchID = batchIDs[drawBatch.key.overridenComponents],
  948. materialID = drawBatch.key.materialID,
  949. splitVisibilityMask = 0x1,
  950. lightmapIndex = (ushort)drawBatch.key.lightmapIndex,
  951. sortingPosition = 0,
  952. meshID = drawBatch.key.meshID,
  953. submeshIndex = (ushort)drawBatch.key.submeshIndex,
  954. };
  955. outVisibleInstanceIndex++;
  956. outCommandIndex++;
  957. }
  958. }
  959. // Emit a DrawRange to the array if we have any visible DrawCommands
  960. var rangeDrawCommandCount = outCommandIndex - rangeDrawCommandOffset;
  961. if (rangeDrawCommandCount > 0)
  962. {
  963. #if DEBUG
  964. if (outRangeIndex >= output.drawRangeCount)
  965. throw new Exception("Exceeding draw range count");
  966. #endif
  967. var rangeKey = drawRangeInfo.key;
  968. output.drawRanges[outRangeIndex] = new BatchDrawRange
  969. {
  970. drawCommandsBegin = (uint)rangeDrawCommandOffset,
  971. drawCommandsCount = (uint)rangeDrawCommandCount,
  972. filterSettings = new BatchFilterSettings
  973. {
  974. renderingLayerMask = rangeKey.renderingLayerMask,
  975. rendererPriority = rangeKey.rendererPriority,
  976. layer = rangeKey.layer,
  977. batchLayer = BatchLayer.InstanceCullingDirect,
  978. motionMode = rangeKey.motionMode,
  979. shadowCastingMode = rangeKey.shadowCastingMode,
  980. receiveShadows = true,
  981. staticShadowCaster = rangeKey.staticShadowCaster,
  982. allDepthSorted = false,
  983. }
  984. };
  985. outRangeIndex++;
  986. }
  987. }
  988. // trim to the number of written ranges/commands/instances
  989. output.drawRangeCount = outRangeIndex;
  990. output.drawCommandCount = outCommandIndex;
  991. output.visibleInstanceCount = outVisibleInstanceIndex;
  992. cullingOutput[0] = output;
  993. }
  994. }
  995. [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
  996. internal struct CullSceneViewHiddenRenderersJob : IJobParallelFor
  997. {
  998. public const int k_BatchSize = 128;
  999. [ReadOnly] public CPUInstanceData.ReadOnly instanceData;
  1000. [ReadOnly] public CPUSharedInstanceData.ReadOnly sharedInstanceData;
  1001. [ReadOnly] public ParallelBitArray hiddenBits;
  1002. [NativeDisableParallelForRestriction] public NativeArray<byte> rendererVisibilityMasks;
  1003. public void Execute(int instanceIndex)
  1004. {
  1005. InstanceHandle instance = instanceData.instances[instanceIndex];
  1006. if (rendererVisibilityMasks[instance.index] > 0)
  1007. {
  1008. int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance);
  1009. if (hiddenBits.Get(sharedInstanceIndex))
  1010. rendererVisibilityMasks[instance.index] = 0;
  1011. }
  1012. }
  1013. }
  1014. #endif
  1015. internal enum InstanceCullerSplitDebugCounter
  1016. {
  1017. VisibleInstances,
  1018. DrawCommands,
  1019. Count,
  1020. }
  1021. internal struct InstanceCullerSplitDebugArray : IDisposable
  1022. {
  1023. private const int MaxSplitCount = 64;
  1024. internal struct Info
  1025. {
  1026. public BatchCullingViewType viewType;
  1027. public int viewInstanceID;
  1028. public int splitIndex;
  1029. }
  1030. private NativeList<Info> m_Info;
  1031. private NativeArray<int> m_Counters;
  1032. private NativeQueue<JobHandle> m_CounterSync;
  1033. public NativeArray<int> Counters { get => m_Counters; }
  1034. public void Init()
  1035. {
  1036. m_Info = new NativeList<Info>(Allocator.Persistent);
  1037. m_Counters = new NativeArray<int>(MaxSplitCount * (int)InstanceCullerSplitDebugCounter.Count, Allocator.Persistent);
  1038. m_CounterSync = new NativeQueue<JobHandle>(Allocator.Persistent);
  1039. }
  1040. public void Dispose()
  1041. {
  1042. m_Info.Dispose();
  1043. m_Counters.Dispose();
  1044. m_CounterSync.Dispose();
  1045. }
  1046. public int TryAddSplits(BatchCullingViewType viewType, int viewInstanceID, int splitCount)
  1047. {
  1048. int baseIndex = m_Info.Length;
  1049. if (baseIndex + splitCount > MaxSplitCount)
  1050. return -1;
  1051. for (int splitIndex = 0; splitIndex < splitCount; ++splitIndex)
  1052. {
  1053. m_Info.Add(new Info()
  1054. {
  1055. viewType = viewType,
  1056. viewInstanceID = viewInstanceID,
  1057. splitIndex = splitIndex,
  1058. });
  1059. }
  1060. return baseIndex;
  1061. }
  1062. public void AddSync(int baseIndex, JobHandle jobHandle)
  1063. {
  1064. if (baseIndex != -1)
  1065. m_CounterSync.Enqueue(jobHandle);
  1066. }
  1067. public void MoveToDebugStatsAndClear(DebugRendererBatcherStats debugStats)
  1068. {
  1069. // wait for stats-writing jobs to complete
  1070. while (m_CounterSync.TryDequeue(out var jobHandle))
  1071. {
  1072. jobHandle.Complete();
  1073. }
  1074. // overwrite debug stats with the latest
  1075. debugStats.instanceCullerStats.Clear();
  1076. for (int index = 0; index < m_Info.Length; ++index)
  1077. {
  1078. var info = m_Info[index];
  1079. int counterBase = index * (int)InstanceCullerSplitDebugCounter.Count;
  1080. debugStats.instanceCullerStats.Add(new InstanceCullerViewStats
  1081. {
  1082. viewType = info.viewType,
  1083. viewInstanceID = info.viewInstanceID,
  1084. splitIndex = info.splitIndex,
  1085. visibleInstances = m_Counters[counterBase + (int)InstanceCullerSplitDebugCounter.VisibleInstances],
  1086. drawCommands = m_Counters[counterBase + (int)InstanceCullerSplitDebugCounter.DrawCommands],
  1087. });
  1088. }
  1089. // clear for next frame
  1090. m_Info.Clear();
  1091. m_Counters.FillArray(0);
  1092. }
  1093. }
  1094. internal struct InstanceOcclusionEventDebugArray : IDisposable
  1095. {
  1096. private const int InitialPassCount = 4;
  1097. private const int MaxPassCount = 64;
  1098. internal struct Info
  1099. {
  1100. public int viewInstanceID;
  1101. public InstanceOcclusionEventType eventType;
  1102. public int occluderVersion;
  1103. public int subviewMask;
  1104. public OcclusionTest occlusionTest;
  1105. public bool HasVersion()
  1106. {
  1107. return eventType == InstanceOcclusionEventType.OccluderUpdate || occlusionTest != OcclusionTest.None;
  1108. }
  1109. }
  1110. internal struct Request
  1111. {
  1112. public UnsafeList<Info> info;
  1113. public AsyncGPUReadbackRequest readback;
  1114. }
  1115. private GraphicsBuffer m_CounterBuffer;
  1116. private UnsafeList<Info> m_PendingInfo;
  1117. private NativeQueue<Request> m_Requests;
  1118. private UnsafeList<Info> m_LatestInfo;
  1119. private NativeArray<int> m_LatestCounters;
  1120. private bool m_HasLatest;
  1121. public GraphicsBuffer CounterBuffer { get => m_CounterBuffer; }
  1122. public void Init()
  1123. {
  1124. m_CounterBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, MaxPassCount * (int)InstanceOcclusionTestDebugCounter.Count, sizeof(uint));
  1125. m_PendingInfo = new UnsafeList<Info>(InitialPassCount, Allocator.Persistent);
  1126. m_Requests = new NativeQueue<Request>(Allocator.Persistent);
  1127. }
  1128. public void Dispose()
  1129. {
  1130. if (m_HasLatest)
  1131. {
  1132. m_LatestInfo.Dispose();
  1133. m_LatestCounters.Dispose();
  1134. m_HasLatest = false;
  1135. }
  1136. while (m_Requests.TryDequeue(out var req))
  1137. {
  1138. req.readback.WaitForCompletion();
  1139. req.info.Dispose();
  1140. }
  1141. m_Requests.Dispose();
  1142. m_PendingInfo.Dispose();
  1143. m_CounterBuffer.Dispose();
  1144. }
  1145. public int TryAdd(int viewInstanceID, InstanceOcclusionEventType eventType, int occluderVersion, int subviewMask, OcclusionTest occlusionTest)
  1146. {
  1147. int passIndex = m_PendingInfo.Length;
  1148. if (passIndex + 1 > MaxPassCount)
  1149. return -1;
  1150. m_PendingInfo.Add(new Info()
  1151. {
  1152. viewInstanceID = viewInstanceID,
  1153. eventType = eventType,
  1154. occluderVersion = occluderVersion,
  1155. subviewMask = subviewMask,
  1156. occlusionTest = occlusionTest,
  1157. });
  1158. return passIndex;
  1159. }
  1160. public void MoveToDebugStatsAndClear(DebugRendererBatcherStats debugStats)
  1161. {
  1162. // commit the pending set of stats
  1163. if (m_PendingInfo.Length > 0)
  1164. {
  1165. m_Requests.Enqueue(new Request
  1166. {
  1167. info = m_PendingInfo,
  1168. readback = AsyncGPUReadback.Request(m_CounterBuffer, m_PendingInfo.Length * (int)InstanceOcclusionTestDebugCounter.Count * sizeof(uint), 0)
  1169. });
  1170. m_PendingInfo = new UnsafeList<Info>(InitialPassCount, Allocator.Persistent);
  1171. }
  1172. // update the latest set of results that are ready
  1173. while (!m_Requests.IsEmpty() && m_Requests.Peek().readback.done)
  1174. {
  1175. var req = m_Requests.Dequeue();
  1176. if (!req.readback.hasError)
  1177. {
  1178. NativeArray<int> src = req.readback.GetData<int>(0);
  1179. if (src.Length == req.info.Length * (int)InstanceOcclusionTestDebugCounter.Count)
  1180. {
  1181. if (m_HasLatest)
  1182. {
  1183. m_LatestInfo.Dispose();
  1184. m_LatestCounters.Dispose();
  1185. m_HasLatest = false;
  1186. }
  1187. m_LatestInfo = req.info;
  1188. m_LatestCounters = new NativeArray<int>(src, Allocator.Persistent);
  1189. m_HasLatest = true;
  1190. }
  1191. }
  1192. }
  1193. // overwrite debug stats with the latest
  1194. debugStats.instanceOcclusionEventStats.Clear();
  1195. if (m_HasLatest)
  1196. {
  1197. for (int index = 0; index < m_LatestInfo.Length; ++index)
  1198. {
  1199. var info = m_LatestInfo[index];
  1200. // make occluder version relative to the first one this frame
  1201. int occluderVersion = -1;
  1202. if (info.HasVersion())
  1203. {
  1204. occluderVersion = 0;
  1205. for (int prevIndex = 0; prevIndex < index; ++prevIndex)
  1206. {
  1207. var prevInfo = m_LatestInfo[prevIndex];
  1208. if (prevInfo.HasVersion() && prevInfo.viewInstanceID == info.viewInstanceID)
  1209. {
  1210. occluderVersion = info.occluderVersion - prevInfo.occluderVersion;
  1211. break;
  1212. }
  1213. }
  1214. }
  1215. int counterBase = index * (int)InstanceOcclusionTestDebugCounter.Count;
  1216. int occludedCounter = m_LatestCounters[counterBase + (int)InstanceOcclusionTestDebugCounter.Occluded];
  1217. int notOccludedCounter = m_LatestCounters[counterBase + (int)InstanceOcclusionTestDebugCounter.NotOccluded];
  1218. debugStats.instanceOcclusionEventStats.Add(new InstanceOcclusionEventStats
  1219. {
  1220. viewInstanceID = info.viewInstanceID,
  1221. eventType = info.eventType,
  1222. occluderVersion = occluderVersion,
  1223. subviewMask = info.subviewMask,
  1224. occlusionTest = info.occlusionTest,
  1225. visibleInstances = notOccludedCounter,
  1226. culledInstances = occludedCounter,
  1227. });
  1228. }
  1229. }
  1230. // clear the GPU buffer for the next frame
  1231. var zeros = new NativeArray<int>(MaxPassCount * (int)InstanceOcclusionTestDebugCounter.Count, Allocator.Temp, NativeArrayOptions.ClearMemory);
  1232. m_CounterBuffer.SetData(zeros);
  1233. zeros.Dispose();
  1234. }
  1235. }
  1236. internal struct InstanceCuller : IDisposable
  1237. {
  1238. //@ Move this in CPUInstanceData.
  1239. private ParallelBitArray m_CompactedVisibilityMasks;
  1240. private JobHandle m_CompactedVisibilityMasksJobsHandle;
  1241. private IndirectBufferContextStorage m_IndirectStorage;
  1242. private OcclusionTestComputeShader m_OcclusionTestShader;
  1243. private int m_ResetDrawArgsKernel;
  1244. private int m_CopyInstancesKernel;
  1245. private int m_CullInstancesKernel;
  1246. private DebugRendererBatcherStats m_DebugStats;
  1247. private InstanceCullerSplitDebugArray m_SplitDebugArray;
  1248. private InstanceOcclusionEventDebugArray m_OcclusionEventDebugArray;
  1249. private ProfilingSampler m_ProfilingSampleInstanceOcclusionTest;
  1250. private NativeArray<InstanceOcclusionCullerShaderVariables> m_ShaderVariables;
  1251. private ComputeBuffer m_ConstantBuffer;
  1252. private CommandBuffer m_CommandBuffer;
  1253. #if UNITY_EDITOR
  1254. private bool m_IsSceneViewCamera;
  1255. private ParallelBitArray m_SceneViewHiddenBits;
  1256. #endif
  1257. private static class ShaderIDs
  1258. {
  1259. public static readonly int InstanceOcclusionCullerShaderVariables = Shader.PropertyToID("InstanceOcclusionCullerShaderVariables");
  1260. public static readonly int _DrawInfo = Shader.PropertyToID("_DrawInfo");
  1261. public static readonly int _InstanceInfo = Shader.PropertyToID("_InstanceInfo");
  1262. public static readonly int _DrawArgs = Shader.PropertyToID("_DrawArgs");
  1263. public static readonly int _InstanceIndices = Shader.PropertyToID("_InstanceIndices");
  1264. public static readonly int _InstanceDataBuffer = Shader.PropertyToID("_InstanceDataBuffer");
  1265. // Debug
  1266. public static readonly int _OccluderDepthPyramid = Shader.PropertyToID("_OccluderDepthPyramid");
  1267. public static readonly int _OcclusionDebugCounters = Shader.PropertyToID("_OcclusionDebugCounters");
  1268. }
  1269. internal void Init(GPUResidentDrawerResources resources, DebugRendererBatcherStats debugStats = null)
  1270. {
  1271. m_IndirectStorage.Init();
  1272. m_OcclusionTestShader.Init(resources.instanceOcclusionCullingKernels);
  1273. m_ResetDrawArgsKernel = m_OcclusionTestShader.cs.FindKernel("ResetDrawArgs");
  1274. m_CopyInstancesKernel = m_OcclusionTestShader.cs.FindKernel("CopyInstances");
  1275. m_CullInstancesKernel = m_OcclusionTestShader.cs.FindKernel("CullInstances");
  1276. m_DebugStats = debugStats;
  1277. m_SplitDebugArray = new InstanceCullerSplitDebugArray();
  1278. m_SplitDebugArray.Init();
  1279. m_OcclusionEventDebugArray = new InstanceOcclusionEventDebugArray();
  1280. m_OcclusionEventDebugArray.Init();
  1281. m_ProfilingSampleInstanceOcclusionTest = new ProfilingSampler("InstanceOcclusionTest");
  1282. m_ShaderVariables = new NativeArray<InstanceOcclusionCullerShaderVariables>(1, Allocator.Persistent);
  1283. m_ConstantBuffer = new ComputeBuffer(1, UnsafeUtility.SizeOf<InstanceOcclusionCullerShaderVariables>(), ComputeBufferType.Constant);
  1284. m_CommandBuffer = new CommandBuffer();
  1285. m_CommandBuffer.name = "EnsureValidOcclusionTestResults";
  1286. }
  1287. private JobHandle CreateFrustumCullingJob(
  1288. in BatchCullingContext cc,
  1289. in CPUInstanceData.ReadOnly instanceData,
  1290. in CPUSharedInstanceData.ReadOnly sharedInstanceData,
  1291. NativeList<LODGroupCullingData> lodGroupCullingData,
  1292. in BinningConfig binningConfig,
  1293. float smallMeshScreenPercentage,
  1294. OcclusionCullingCommon occlusionCullingCommon,
  1295. NativeArray<byte> rendererVisibilityMasks,
  1296. NativeArray<byte> rendererCrossFadeValues)
  1297. {
  1298. Assert.IsTrue(cc.cullingSplits.Length <= 6, "InstanceCullingBatcher supports up to 6 culling splits.");
  1299. var receiverPlanes = ReceiverPlanes.Create(cc, Allocator.Temp);
  1300. var receiverSphereCuller = ReceiverSphereCuller.Create(cc, Allocator.TempJob);
  1301. var frustumPlaneCuller = FrustumPlaneCuller.Create(cc, receiverPlanes.planes.AsArray(), receiverSphereCuller, Allocator.TempJob);
  1302. var lightFacingFrustumPlanes = receiverPlanes.CopyLightFacingFrustumPlanes(Allocator.TempJob);
  1303. if (occlusionCullingCommon != null)
  1304. occlusionCullingCommon.UpdateSilhouettePlanes(cc.viewID.GetInstanceID(), receiverPlanes.SilhouettePlaneSubArray());
  1305. receiverPlanes.planes.Dispose();
  1306. float screenRelativeMetric = LODGroupRenderingUtils.CalculateScreenRelativeMetric(cc.lodParameters);
  1307. var cullingJob = new CullingJob
  1308. {
  1309. binningConfig = binningConfig,
  1310. viewType = cc.viewType,
  1311. frustumPlanePackets = frustumPlaneCuller.planePackets,
  1312. frustumSplitInfos = frustumPlaneCuller.splitInfos,
  1313. lightFacingFrustumPlanes = lightFacingFrustumPlanes,
  1314. receiverSplitInfos = receiverSphereCuller.splitInfos,
  1315. worldToLightSpaceRotation = receiverSphereCuller.worldToLightSpaceRotation,
  1316. cullLightmappedShadowCasters = (cc.cullingFlags & BatchCullingFlags.CullLightmappedShadowCasters) != 0,
  1317. cameraPosition = cc.lodParameters.cameraPosition,
  1318. sqrScreenRelativeMetric = screenRelativeMetric * screenRelativeMetric,
  1319. minScreenRelativeHeight = smallMeshScreenPercentage * 0.01f,
  1320. isOrtho = cc.lodParameters.isOrthographic,
  1321. instanceData = instanceData,
  1322. sharedInstanceData = sharedInstanceData,
  1323. lodGroupCullingData = lodGroupCullingData,
  1324. occlusionBuffer = cc.occlusionBuffer,
  1325. rendererVisibilityMasks = rendererVisibilityMasks,
  1326. rendererCrossFadeValues = rendererCrossFadeValues,
  1327. maxLOD = QualitySettings.maximumLODLevel,
  1328. cullingLayerMask = cc.cullingLayerMask,
  1329. sceneCullingMask = cc.sceneCullingMask,
  1330. };
  1331. return cullingJob.Schedule(instanceData.instancesLength, CullingJob.k_BatchSize);
  1332. }
  1333. private int ComputeWorstCaseDrawCommandCount(
  1334. in BatchCullingContext cc,
  1335. BinningConfig binningConfig,
  1336. CPUDrawInstanceData drawInstanceData,
  1337. int crossFadedRendererCount)
  1338. {
  1339. int visibleInstancesCount = drawInstanceData.drawInstances.Length;
  1340. int drawCommandCount = drawInstanceData.drawBatches.Length;
  1341. // add the number of batches split due to actively cross-fading
  1342. drawCommandCount += math.min(crossFadedRendererCount, drawCommandCount);
  1343. // batches can be split due to flip winding
  1344. drawCommandCount *= 2;
  1345. // and actively moving
  1346. if (binningConfig.supportsMotionCheck)
  1347. drawCommandCount *= 2;
  1348. if (cc.cullingSplits.Length > 1)
  1349. {
  1350. // visible instances are only written once, grouped by visibility mask bit pattern
  1351. // draw calls are split for each unique visibility mask bit pattern
  1352. // handle the worst case where each draw has an instance for every possible mask
  1353. drawCommandCount <<= (cc.cullingSplits.Length - 1);
  1354. }
  1355. // empty draw commands are skipped, so there cannot be more draw commands than visible instances
  1356. drawCommandCount = math.min(drawCommandCount, visibleInstancesCount);
  1357. return drawCommandCount;
  1358. }
  1359. public unsafe JobHandle CreateCullJobTree(
  1360. in BatchCullingContext cc,
  1361. BatchCullingOutput cullingOutput,
  1362. in CPUInstanceData.ReadOnly instanceData,
  1363. in CPUSharedInstanceData.ReadOnly sharedInstanceData,
  1364. in GPUInstanceDataBuffer.ReadOnly instanceDataBuffer,
  1365. NativeList<LODGroupCullingData> lodGroupCullingData,
  1366. CPUDrawInstanceData drawInstanceData,
  1367. NativeParallelHashMap<uint, BatchID> batchIDs,
  1368. int crossFadedRendererCount,
  1369. float smallMeshScreenPercentage,
  1370. OcclusionCullingCommon occlusionCullingCommon)
  1371. {
  1372. // allocate for worst case number of draw ranges (all other arrays allocated after size is known)
  1373. var drawCommands = new BatchCullingOutputDrawCommands();
  1374. drawCommands.drawRangeCount = drawInstanceData.drawRanges.Length;
  1375. drawCommands.drawRanges = MemoryUtilities.Malloc<BatchDrawRange>(drawCommands.drawRangeCount, Allocator.TempJob);
  1376. for (int i = 0; i < drawCommands.drawRangeCount; ++i)
  1377. drawCommands.drawRanges[i].drawCommandsCount = 0;
  1378. cullingOutput.drawCommands[0] = drawCommands;
  1379. cullingOutput.customCullingResult[0] = IntPtr.Zero;
  1380. var binningConfig = new BinningConfig
  1381. {
  1382. viewCount = cc.cullingSplits.Length,
  1383. supportsCrossFade = (crossFadedRendererCount > 0),
  1384. supportsMotionCheck = (cc.viewType == BatchCullingViewType.Camera), // TODO: could disable here if RP never needs object motion vectors, for now always batch on it
  1385. };
  1386. var visibilityLength = instanceData.handlesLength;
  1387. var rendererVisibilityMasks = new NativeArray<byte>(visibilityLength, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
  1388. var rendererCrossFadeValues = new NativeArray<byte>(visibilityLength, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
  1389. var cullingJobHandle = CreateFrustumCullingJob(cc, instanceData, sharedInstanceData, lodGroupCullingData, binningConfig,
  1390. smallMeshScreenPercentage, occlusionCullingCommon, rendererVisibilityMasks, rendererCrossFadeValues);
  1391. #if UNITY_EDITOR
  1392. // Unfortunately BatchCullingContext doesn't provide full visibility and picking context.
  1393. // Including which object is hidden in the hierarchy panel or not pickable in the scene view for tooling purposes.
  1394. // So we have to manually handle bold editor logic here inside the culler.
  1395. // This should be redesigned in the future. Culler should not be responsible for custom editor handling logic or even know that the editor exist.
  1396. // This additionally culls game objects hidden in the hierarchy panel or the scene view or in context editing.
  1397. cullingJobHandle = CreateSceneViewHiddenObjectsCullingJob_EditorOnly(cc, instanceData, sharedInstanceData, rendererVisibilityMasks,
  1398. cullingJobHandle);
  1399. if (cc.viewType == BatchCullingViewType.Picking)
  1400. {
  1401. // This outputs picking draw commands for the objects that can be picked.
  1402. cullingJobHandle = CreatePickingCullingOutputJob_EditorOnly(cc, cullingOutput, instanceData, sharedInstanceData, instanceDataBuffer,
  1403. drawInstanceData, batchIDs, rendererVisibilityMasks, rendererCrossFadeValues, cullingJobHandle);
  1404. }
  1405. else if (cc.viewType == BatchCullingViewType.Filtering)
  1406. {
  1407. // This outputs draw commands for the objects filtered by search input in the hierarchy on in the scene view.
  1408. cullingJobHandle = CreateFilteringCullingOutputJob_EditorOnly(cc, cullingOutput, instanceData, sharedInstanceData, instanceDataBuffer, drawInstanceData,
  1409. batchIDs, rendererVisibilityMasks, rendererCrossFadeValues, cullingJobHandle);
  1410. }
  1411. #endif
  1412. // This outputs regular draw commands.
  1413. if (cc.viewType == BatchCullingViewType.Camera || cc.viewType == BatchCullingViewType.Light || cc.viewType == BatchCullingViewType.SelectionOutline)
  1414. {
  1415. cullingJobHandle = CreateCompactedVisibilityMaskJob(instanceData, rendererVisibilityMasks, cullingJobHandle);
  1416. int debugCounterBaseIndex = -1;
  1417. if (m_DebugStats?.enabled ?? false)
  1418. {
  1419. debugCounterBaseIndex = m_SplitDebugArray.TryAddSplits(cc.viewType, cc.viewID.GetInstanceID(), cc.cullingSplits.Length);
  1420. }
  1421. var batchCount = drawInstanceData.drawBatches.Length;
  1422. int maxBinCount = ComputeWorstCaseDrawCommandCount(cc, binningConfig, drawInstanceData, crossFadedRendererCount);
  1423. var batchBinAllocOffsets = new NativeArray<int>(batchCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
  1424. var batchBinCounts = new NativeArray<int>(batchCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
  1425. var batchDrawCommandOffsets = new NativeArray<int>(batchCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
  1426. var binAllocCounter = new NativeArray<int>(JobsUtility.CacheLineSize / sizeof(int), Allocator.TempJob);
  1427. var binConfigIndices = new NativeArray<short>(maxBinCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
  1428. var binVisibleInstanceCounts = new NativeArray<int>(maxBinCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
  1429. var binVisibleInstanceOffsets = new NativeArray<int>(maxBinCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
  1430. int indirectContextIndex = -1;
  1431. bool useOcclusionCulling = (occlusionCullingCommon != null) && occlusionCullingCommon.HasOccluderContext(cc.viewID.GetInstanceID());
  1432. if (useOcclusionCulling)
  1433. {
  1434. int viewInstanceID = cc.viewID.GetInstanceID();
  1435. indirectContextIndex = m_IndirectStorage.TryAllocateContext(viewInstanceID);
  1436. cullingOutput.customCullingResult[0] = (IntPtr)viewInstanceID;
  1437. }
  1438. IndirectBufferLimits indirectBufferLimits = m_IndirectStorage.GetLimits(indirectContextIndex);
  1439. NativeArray<IndirectBufferAllocInfo> indirectBufferAllocInfo = m_IndirectStorage.GetAllocInfoSubArray(indirectContextIndex);
  1440. var allocateBinsJob = new AllocateBinsPerBatch
  1441. {
  1442. binningConfig = binningConfig,
  1443. drawBatches = drawInstanceData.drawBatches,
  1444. drawInstanceIndices = drawInstanceData.drawInstanceIndices,
  1445. instanceData = instanceData,
  1446. rendererVisibilityMasks = rendererVisibilityMasks,
  1447. batchBinAllocOffsets = batchBinAllocOffsets,
  1448. batchBinCounts = batchBinCounts,
  1449. binAllocCounter = binAllocCounter,
  1450. binConfigIndices = binConfigIndices,
  1451. binVisibleInstanceCounts = binVisibleInstanceCounts,
  1452. splitDebugCounters = m_SplitDebugArray.Counters,
  1453. debugCounterIndexBase = debugCounterBaseIndex,
  1454. };
  1455. var allocateBinsHandle = allocateBinsJob.Schedule(batchCount, 1, cullingJobHandle);
  1456. m_SplitDebugArray.AddSync(debugCounterBaseIndex, allocateBinsHandle);
  1457. var prefixSumJob = new PrefixSumDrawsAndInstances
  1458. {
  1459. drawRanges = drawInstanceData.drawRanges,
  1460. drawBatchIndices = drawInstanceData.drawBatchIndices,
  1461. batchBinAllocOffsets = batchBinAllocOffsets,
  1462. batchBinCounts = batchBinCounts,
  1463. binVisibleInstanceCounts = binVisibleInstanceCounts,
  1464. batchDrawCommandOffsets = batchDrawCommandOffsets,
  1465. binVisibleInstanceOffsets = binVisibleInstanceOffsets,
  1466. cullingOutput = cullingOutput.drawCommands,
  1467. indirectBufferLimits = indirectBufferLimits,
  1468. indirectBufferAllocInfo = indirectBufferAllocInfo,
  1469. indirectAllocationCounters = m_IndirectStorage.allocationCounters,
  1470. };
  1471. var prefixSumHandle = prefixSumJob.Schedule(allocateBinsHandle);
  1472. var drawCommandOutputJob = new DrawCommandOutputPerBatch
  1473. {
  1474. binningConfig = binningConfig,
  1475. batchIDs = batchIDs,
  1476. instanceDataBuffer = instanceDataBuffer,
  1477. drawBatches = drawInstanceData.drawBatches,
  1478. drawInstanceIndices = drawInstanceData.drawInstanceIndices,
  1479. instanceData = instanceData,
  1480. rendererVisibilityMasks = rendererVisibilityMasks,
  1481. rendererCrossFadeValues = rendererCrossFadeValues,
  1482. batchBinAllocOffsets = batchBinAllocOffsets,
  1483. batchBinCounts = batchBinCounts,
  1484. batchDrawCommandOffsets = batchDrawCommandOffsets,
  1485. binConfigIndices = binConfigIndices,
  1486. binVisibleInstanceOffsets = binVisibleInstanceOffsets,
  1487. binVisibleInstanceCounts = binVisibleInstanceCounts,
  1488. cullingOutput = cullingOutput.drawCommands,
  1489. indirectBufferLimits = indirectBufferLimits,
  1490. visibleInstancesBufferHandle = m_IndirectStorage.visibleInstanceBufferHandle,
  1491. indirectArgsBufferHandle = m_IndirectStorage.indirectArgsBufferHandle,
  1492. indirectBufferAllocInfo = indirectBufferAllocInfo,
  1493. indirectInstanceInfoGlobalArray = m_IndirectStorage.instanceInfoGlobalArray,
  1494. indirectDrawInfoGlobalArray = m_IndirectStorage.drawInfoGlobalArray,
  1495. };
  1496. var drawCommandOutputHandle = drawCommandOutputJob.Schedule(batchCount, 1, prefixSumHandle);
  1497. if (useOcclusionCulling)
  1498. m_IndirectStorage.SetBufferContext(indirectContextIndex, new IndirectBufferContext(drawCommandOutputHandle));
  1499. cullingJobHandle = drawCommandOutputHandle;
  1500. }
  1501. cullingJobHandle = rendererVisibilityMasks.Dispose(cullingJobHandle);
  1502. cullingJobHandle = rendererCrossFadeValues.Dispose(cullingJobHandle);
  1503. return cullingJobHandle;
  1504. }
  1505. private JobHandle CreateCompactedVisibilityMaskJob(in CPUInstanceData.ReadOnly instanceData, NativeArray<byte> rendererVisibilityMasks, JobHandle cullingJobHandle)
  1506. {
  1507. if (!m_CompactedVisibilityMasks.IsCreated)
  1508. {
  1509. Assert.IsTrue(m_CompactedVisibilityMasksJobsHandle.IsCompleted);
  1510. m_CompactedVisibilityMasks = new ParallelBitArray(instanceData.handlesLength, Allocator.TempJob);
  1511. }
  1512. var compactVisibilityMasksJob = new CompactVisibilityMasksJob
  1513. {
  1514. rendererVisibilityMasks = rendererVisibilityMasks,
  1515. compactedVisibilityMasks = m_CompactedVisibilityMasks
  1516. };
  1517. var compactVisibilityMasksJobHandle = compactVisibilityMasksJob.ScheduleBatch(rendererVisibilityMasks.Length, CompactVisibilityMasksJob.k_BatchSize, cullingJobHandle);
  1518. m_CompactedVisibilityMasksJobsHandle = JobHandle.CombineDependencies(m_CompactedVisibilityMasksJobsHandle, compactVisibilityMasksJobHandle);
  1519. return compactVisibilityMasksJobHandle;
  1520. }
  1521. #if UNITY_EDITOR
  1522. private JobHandle CreateSceneViewHiddenObjectsCullingJob_EditorOnly(in BatchCullingContext cc, in CPUInstanceData.ReadOnly instanceData,
  1523. in CPUSharedInstanceData.ReadOnly sharedInstanceData, NativeArray<byte> rendererVisibilityMasks, JobHandle cullingJobHandle)
  1524. {
  1525. bool isSceneViewCamera = m_IsSceneViewCamera && (cc.viewType == BatchCullingViewType.Camera || cc.viewType == BatchCullingViewType.Light);
  1526. bool isEditorCullingViewType = cc.viewType == BatchCullingViewType.Picking || cc.viewType == BatchCullingViewType.SelectionOutline
  1527. || cc.viewType == BatchCullingViewType.Filtering;
  1528. if (!isSceneViewCamera && !isEditorCullingViewType)
  1529. return cullingJobHandle;
  1530. bool isEditingPrefab = PrefabStageUtility.GetCurrentPrefabStage() != null;
  1531. bool isAnyObjectHidden = false;
  1532. for (int i = 0; i < SceneManager.sceneCount; ++i)
  1533. {
  1534. Scene scene = SceneManager.GetSceneAt(i);
  1535. if (SceneVisibilityManager.instance.AreAnyDescendantsHidden(scene))
  1536. {
  1537. isAnyObjectHidden = true;
  1538. break;
  1539. }
  1540. }
  1541. if (!isAnyObjectHidden && !isEditingPrefab)
  1542. return cullingJobHandle;
  1543. int renderersLength = sharedInstanceData.rendererGroupIDs.Length;
  1544. if (!m_SceneViewHiddenBits.IsCreated)
  1545. {
  1546. m_SceneViewHiddenBits = new ParallelBitArray(renderersLength, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
  1547. EditorCameraUtils.GetRenderersHiddenResultBits(sharedInstanceData.rendererGroupIDs, m_SceneViewHiddenBits.GetBitsArray().Reinterpret<ulong>());
  1548. }
  1549. var jobHandle = new CullSceneViewHiddenRenderersJob
  1550. {
  1551. instanceData = instanceData,
  1552. sharedInstanceData = sharedInstanceData,
  1553. rendererVisibilityMasks = rendererVisibilityMasks,
  1554. hiddenBits = m_SceneViewHiddenBits,
  1555. }.Schedule(instanceData.instancesLength, CullSceneViewHiddenRenderersJob.k_BatchSize, cullingJobHandle);
  1556. return jobHandle;
  1557. }
  1558. private JobHandle CreateFilteringCullingOutputJob_EditorOnly(in BatchCullingContext cc, BatchCullingOutput cullingOutput,
  1559. in CPUInstanceData.ReadOnly instanceData, in CPUSharedInstanceData.ReadOnly sharedInstanceData, in GPUInstanceDataBuffer.ReadOnly instanceDataBuffer,
  1560. in CPUDrawInstanceData drawInstanceData, NativeParallelHashMap<uint, BatchID> batchIDs, NativeArray<byte> rendererVisibilityMasks,
  1561. NativeArray<byte> rendererCrossFadeValues, JobHandle cullingJobHandle)
  1562. {
  1563. NativeArray<bool> filteredRenderers = new NativeArray<bool>(sharedInstanceData.rendererGroupIDs.Length, Allocator.TempJob);
  1564. EditorCameraUtils.GetRenderersFilteringResults(sharedInstanceData.rendererGroupIDs, filteredRenderers);
  1565. var dummyExcludedRenderers = new NativeArray<int>(0, Allocator.TempJob);
  1566. var drawOutputJob = new DrawCommandOutputFiltering
  1567. {
  1568. viewID = cc.viewID.GetInstanceID(),
  1569. batchIDs = batchIDs,
  1570. instanceDataBuffer = instanceDataBuffer,
  1571. rendererVisibilityMasks = rendererVisibilityMasks,
  1572. rendererCrossFadeValues = rendererCrossFadeValues,
  1573. instanceData = instanceData,
  1574. sharedInstanceData = sharedInstanceData,
  1575. drawInstanceIndices = drawInstanceData.drawInstanceIndices,
  1576. drawBatches = drawInstanceData.drawBatches,
  1577. drawRanges = drawInstanceData.drawRanges,
  1578. drawBatchIndices = drawInstanceData.drawBatchIndices,
  1579. filteringResults = filteredRenderers,
  1580. excludedRenderers = dummyExcludedRenderers,
  1581. cullingOutput = cullingOutput.drawCommands,
  1582. mode = FilteringJobMode.Filtering
  1583. };
  1584. var drawOutputHandle = drawOutputJob.Schedule(cullingJobHandle);
  1585. filteredRenderers.Dispose(drawOutputHandle);
  1586. dummyExcludedRenderers.Dispose(drawOutputHandle);
  1587. return drawOutputHandle;
  1588. }
  1589. private JobHandle CreatePickingCullingOutputJob_EditorOnly(in BatchCullingContext cc, BatchCullingOutput cullingOutput,
  1590. in CPUInstanceData.ReadOnly instanceData, in CPUSharedInstanceData.ReadOnly sharedInstanceData, in GPUInstanceDataBuffer.ReadOnly instanceDataBuffer,
  1591. in CPUDrawInstanceData drawInstanceData, NativeParallelHashMap<uint, BatchID> batchIDs, NativeArray<byte> rendererVisibilityMasks,
  1592. NativeArray<byte> rendererCrossFadeValues, JobHandle cullingJobHandle)
  1593. {
  1594. // GPUResindetDrawer doesn't handle rendering of persistent game objects like prefabs. They are rendered by SRP.
  1595. // When we are in prefab editing mode all the objects that are not part of the prefab should not be pickable.
  1596. if (PrefabStageUtility.GetCurrentPrefabStage() != null)
  1597. return cullingJobHandle;
  1598. var pickingIDs = HandleUtility.GetPickingIncludeExcludeList(Allocator.TempJob);
  1599. var excludedRenderers = pickingIDs.ExcludeRenderers.IsCreated ? pickingIDs.ExcludeRenderers : new NativeArray<int>(0, Allocator.TempJob);
  1600. var dummyFilteringResults = new NativeArray<bool>(0, Allocator.TempJob);
  1601. var drawOutputJob = new DrawCommandOutputFiltering
  1602. {
  1603. viewID = cc.viewID.GetInstanceID(),
  1604. batchIDs = batchIDs,
  1605. instanceDataBuffer = instanceDataBuffer,
  1606. rendererVisibilityMasks = rendererVisibilityMasks,
  1607. rendererCrossFadeValues = rendererCrossFadeValues,
  1608. instanceData = instanceData,
  1609. sharedInstanceData = sharedInstanceData,
  1610. drawInstanceIndices = drawInstanceData.drawInstanceIndices,
  1611. drawBatches = drawInstanceData.drawBatches,
  1612. drawRanges = drawInstanceData.drawRanges,
  1613. drawBatchIndices = drawInstanceData.drawBatchIndices,
  1614. filteringResults = dummyFilteringResults,
  1615. excludedRenderers = excludedRenderers,
  1616. cullingOutput = cullingOutput.drawCommands,
  1617. mode = FilteringJobMode.Picking
  1618. };
  1619. var drawOutputHandle = drawOutputJob.Schedule(cullingJobHandle);
  1620. drawOutputHandle.Complete();
  1621. dummyFilteringResults.Dispose();
  1622. if (!pickingIDs.ExcludeRenderers.IsCreated)
  1623. excludedRenderers.Dispose();
  1624. pickingIDs.Dispose();
  1625. return drawOutputHandle;
  1626. }
  1627. #endif
  1628. public void InstanceOccludersUpdated(int viewInstanceID, int subviewMask, RenderersBatchersContext batchersContext)
  1629. {
  1630. if (m_DebugStats?.enabled ?? false)
  1631. {
  1632. var occlusionCullingCommon = batchersContext.occlusionCullingCommon;
  1633. bool hasOccluders = occlusionCullingCommon.GetOccluderContext(viewInstanceID, out OccluderContext occluderCtx);
  1634. if (hasOccluders)
  1635. {
  1636. m_OcclusionEventDebugArray.TryAdd(
  1637. viewInstanceID,
  1638. InstanceOcclusionEventType.OccluderUpdate,
  1639. occluderCtx.version,
  1640. subviewMask,
  1641. OcclusionTest.None);
  1642. }
  1643. }
  1644. }
  1645. private void DisposeCompactVisibilityMasks()
  1646. {
  1647. if (m_CompactedVisibilityMasks.IsCreated)
  1648. {
  1649. Assert.IsTrue(m_CompactedVisibilityMasksJobsHandle.IsCompleted);
  1650. m_CompactedVisibilityMasks.Dispose();
  1651. }
  1652. }
  1653. private void DisposeSceneViewHiddenBits()
  1654. {
  1655. #if UNITY_EDITOR
  1656. if (m_SceneViewHiddenBits.IsCreated)
  1657. m_SceneViewHiddenBits.Dispose();
  1658. #endif
  1659. }
  1660. public ParallelBitArray GetCompactedVisibilityMasks(bool syncCullingJobs)
  1661. {
  1662. if (syncCullingJobs)
  1663. m_CompactedVisibilityMasksJobsHandle.Complete();
  1664. return m_CompactedVisibilityMasks;
  1665. }
  1666. private class InstanceOcclusionTestPassData
  1667. {
  1668. public OcclusionCullingSettings settings;
  1669. public InstanceOcclusionTestSubviewSettings subviewSettings;
  1670. public OccluderHandles occluderHandles;
  1671. public IndirectBufferContextHandles bufferHandles;
  1672. }
  1673. public void InstanceOcclusionTest(RenderGraph renderGraph, in OcclusionCullingSettings settings, ReadOnlySpan<SubviewOcclusionTest> subviewOcclusionTests, RenderersBatchersContext batchersContext)
  1674. {
  1675. if (!batchersContext.occlusionCullingCommon.GetOccluderContext(settings.viewInstanceID, out OccluderContext occluderCtx))
  1676. return;
  1677. var occluderHandles = occluderCtx.Import(renderGraph);
  1678. if (!occluderHandles.IsValid())
  1679. return;
  1680. using (var builder = renderGraph.AddComputePass<InstanceOcclusionTestPassData>("Instance Occlusion Test", out var passData, m_ProfilingSampleInstanceOcclusionTest))
  1681. {
  1682. builder.AllowGlobalStateModification(true);
  1683. passData.settings = settings;
  1684. passData.subviewSettings = InstanceOcclusionTestSubviewSettings.FromSpan(subviewOcclusionTests);
  1685. passData.bufferHandles = m_IndirectStorage.ImportBuffers(renderGraph);
  1686. passData.occluderHandles = occluderHandles;
  1687. passData.bufferHandles.UseForOcclusionTest(builder);
  1688. passData.occluderHandles.UseForOcclusionTest(builder);
  1689. builder.SetRenderFunc((InstanceOcclusionTestPassData data, ComputeGraphContext context) =>
  1690. {
  1691. var batcher = GPUResidentDrawer.instance.batcher;
  1692. batcher.instanceCullingBatcher.culler.AddOcclusionCullingDispatch(
  1693. context.cmd,
  1694. data.settings,
  1695. data.subviewSettings,
  1696. data.bufferHandles,
  1697. data.occluderHandles,
  1698. batcher.batchersContext);
  1699. });
  1700. }
  1701. }
  1702. internal void EnsureValidOcclusionTestResults(int viewInstanceID)
  1703. {
  1704. int indirectContextIndex = m_IndirectStorage.TryGetContextIndex(viewInstanceID);
  1705. if (indirectContextIndex >= 0)
  1706. {
  1707. // sync before checking the allocation results
  1708. IndirectBufferContext bufferCtx = m_IndirectStorage.GetBufferContext(indirectContextIndex);
  1709. if (bufferCtx.bufferState == IndirectBufferContext.BufferState.Pending)
  1710. bufferCtx.cullingJobHandle.Complete();
  1711. // if this did allocate, then ensure the indirect args start with valid data that renders everything
  1712. IndirectBufferAllocInfo allocInfo = m_IndirectStorage.GetAllocInfo(indirectContextIndex);
  1713. if (!allocInfo.IsEmpty())
  1714. {
  1715. var cmd = m_CommandBuffer;
  1716. cmd.Clear();
  1717. m_IndirectStorage.CopyFromStaging(cmd, allocInfo);
  1718. var cs = m_OcclusionTestShader.cs;
  1719. m_ShaderVariables[0] = new InstanceOcclusionCullerShaderVariables
  1720. {
  1721. _DrawInfoAllocIndex = (uint)allocInfo.drawAllocIndex,
  1722. _DrawInfoCount = (uint)allocInfo.drawCount,
  1723. _InstanceInfoAllocIndex = (uint)(IndirectBufferContextStorage.kInstanceInfoGpuOffsetMultiplier * allocInfo.instanceAllocIndex),
  1724. _InstanceInfoCount = (uint)allocInfo.instanceCount,
  1725. _BoundingSphereInstanceDataAddress = 0,
  1726. _DebugCounterIndex = -1,
  1727. _InstanceMultiplierShift = 0,
  1728. };
  1729. cmd.SetBufferData(m_ConstantBuffer, m_ShaderVariables);
  1730. cmd.SetComputeConstantBufferParam(cs, ShaderIDs.InstanceOcclusionCullerShaderVariables, m_ConstantBuffer, 0, m_ConstantBuffer.stride);
  1731. int kernel = m_CopyInstancesKernel;
  1732. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._DrawInfo, m_IndirectStorage.drawInfoBuffer);
  1733. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._InstanceInfo, m_IndirectStorage.instanceInfoBuffer);
  1734. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._DrawArgs, m_IndirectStorage.argsBuffer);
  1735. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._InstanceIndices, m_IndirectStorage.instanceBuffer);
  1736. cmd.DispatchCompute(cs, kernel, (allocInfo.instanceCount + 63) / 64, 1, 1);
  1737. Graphics.ExecuteCommandBuffer(cmd);
  1738. cmd.Clear();
  1739. }
  1740. }
  1741. }
  1742. private void AddOcclusionCullingDispatch(
  1743. ComputeCommandBuffer cmd,
  1744. in OcclusionCullingSettings settings,
  1745. in InstanceOcclusionTestSubviewSettings subviewSettings,
  1746. in IndirectBufferContextHandles bufferHandles,
  1747. in OccluderHandles occluderHandles,
  1748. RenderersBatchersContext batchersContext)
  1749. {
  1750. var occlusionCullingCommon = batchersContext.occlusionCullingCommon;
  1751. int indirectContextIndex = m_IndirectStorage.TryGetContextIndex(settings.viewInstanceID);
  1752. if (indirectContextIndex >= 0)
  1753. {
  1754. IndirectBufferContext bufferCtx = m_IndirectStorage.GetBufferContext(indirectContextIndex);
  1755. // check what compute we need to do (if any)
  1756. bool hasOccluders = occlusionCullingCommon.GetOccluderContext(settings.viewInstanceID, out OccluderContext occluderCtx);
  1757. // check we have occluders for all the required subviews, disable the occlusion test if not
  1758. hasOccluders = hasOccluders && ((subviewSettings.occluderSubviewMask & occluderCtx.subviewValidMask) == subviewSettings.occluderSubviewMask);
  1759. IndirectBufferContext.BufferState newBufferState = IndirectBufferContext.BufferState.Zeroed;
  1760. int newOccluderVersion = 0;
  1761. int newSubviewMask = 0;
  1762. switch (settings.occlusionTest)
  1763. {
  1764. case OcclusionTest.None:
  1765. newBufferState = IndirectBufferContext.BufferState.NoOcclusionTest;
  1766. break;
  1767. case OcclusionTest.TestAll:
  1768. if (hasOccluders)
  1769. {
  1770. newBufferState = IndirectBufferContext.BufferState.AllInstancesOcclusionTested;
  1771. newOccluderVersion = occluderCtx.version;
  1772. newSubviewMask = subviewSettings.occluderSubviewMask;
  1773. }
  1774. else
  1775. {
  1776. newBufferState = IndirectBufferContext.BufferState.NoOcclusionTest;
  1777. }
  1778. break;
  1779. case OcclusionTest.TestCulled:
  1780. if (hasOccluders)
  1781. {
  1782. bool hasMatchingCullingOutput = true;
  1783. switch (bufferCtx.bufferState)
  1784. {
  1785. case IndirectBufferContext.BufferState.AllInstancesOcclusionTested:
  1786. case IndirectBufferContext.BufferState.OccludedInstancesReTested:
  1787. // valid or already done
  1788. if (bufferCtx.subviewMask != subviewSettings.occluderSubviewMask)
  1789. {
  1790. Debug.Log("Expected an occlusion test of TestCulled to use the same subview mask as the previous occlusion test");
  1791. hasMatchingCullingOutput = false;
  1792. }
  1793. break;
  1794. case IndirectBufferContext.BufferState.NoOcclusionTest:
  1795. case IndirectBufferContext.BufferState.Zeroed:
  1796. // no instances, keep the new buffer state zeroed
  1797. hasMatchingCullingOutput = false;
  1798. break;
  1799. default:
  1800. // unexpected, keep the new buffer state zeroed
  1801. hasMatchingCullingOutput = false;
  1802. Debug.Log("Expected the previous occlusion test to be TestAll before using TestCulled");
  1803. break;
  1804. }
  1805. if (hasMatchingCullingOutput)
  1806. {
  1807. newBufferState = IndirectBufferContext.BufferState.OccludedInstancesReTested;
  1808. newOccluderVersion = occluderCtx.version;
  1809. newSubviewMask = subviewSettings.occluderSubviewMask;
  1810. }
  1811. }
  1812. break;
  1813. }
  1814. // issue the work (if any)
  1815. if (!bufferCtx.Matches(newBufferState, newOccluderVersion, newSubviewMask))
  1816. {
  1817. bool isFirstPass = (newBufferState == IndirectBufferContext.BufferState.AllInstancesOcclusionTested);
  1818. bool isSecondPass = (newBufferState == IndirectBufferContext.BufferState.OccludedInstancesReTested);
  1819. bool doWait = (bufferCtx.bufferState == IndirectBufferContext.BufferState.Pending);
  1820. bool doCopyInstances = (newBufferState == IndirectBufferContext.BufferState.NoOcclusionTest);
  1821. bool doResetDraws = (bufferCtx.bufferState != IndirectBufferContext.BufferState.Zeroed) && !doCopyInstances;
  1822. bool doCullInstances = (newBufferState != IndirectBufferContext.BufferState.Zeroed) && !doCopyInstances;
  1823. // sync before checking the allocation results
  1824. if (doWait)
  1825. bufferCtx.cullingJobHandle.Complete();
  1826. IndirectBufferAllocInfo allocInfo = m_IndirectStorage.GetAllocInfo(indirectContextIndex);
  1827. bufferCtx.bufferState = newBufferState;
  1828. bufferCtx.occluderVersion = newOccluderVersion;
  1829. bufferCtx.subviewMask = newSubviewMask;
  1830. if (!allocInfo.IsEmpty())
  1831. {
  1832. int debugCounterIndex = -1;
  1833. if (m_DebugStats?.enabled ?? false)
  1834. {
  1835. debugCounterIndex = m_OcclusionEventDebugArray.TryAdd(
  1836. settings.viewInstanceID,
  1837. InstanceOcclusionEventType.OcclusionTest,
  1838. newOccluderVersion,
  1839. newSubviewMask,
  1840. isFirstPass ? OcclusionTest.TestAll : isSecondPass ? OcclusionTest.TestCulled : OcclusionTest.None);
  1841. }
  1842. // set up keywords
  1843. bool occlusionDebug = false;
  1844. if (isFirstPass || isSecondPass)
  1845. {
  1846. occlusionDebug = OcclusionCullingCommon.UseOcclusionDebug(in occluderCtx) && occluderHandles.occlusionDebugOverlay.IsValid();
  1847. }
  1848. var cs = m_OcclusionTestShader.cs;
  1849. var firstPassKeyword = new LocalKeyword(cs, "OCCLUSION_FIRST_PASS");
  1850. var secondPassKeyword = new LocalKeyword(cs, "OCCLUSION_SECOND_PASS");
  1851. OccluderContext.SetKeyword(cmd, cs, firstPassKeyword, isFirstPass);
  1852. OccluderContext.SetKeyword(cmd, cs, secondPassKeyword, isSecondPass);
  1853. m_ShaderVariables[0] = new InstanceOcclusionCullerShaderVariables
  1854. {
  1855. _DrawInfoAllocIndex = (uint)allocInfo.drawAllocIndex,
  1856. _DrawInfoCount = (uint)allocInfo.drawCount,
  1857. _InstanceInfoAllocIndex = (uint)(IndirectBufferContextStorage.kInstanceInfoGpuOffsetMultiplier * allocInfo.instanceAllocIndex),
  1858. _InstanceInfoCount = (uint)allocInfo.instanceCount,
  1859. _BoundingSphereInstanceDataAddress = batchersContext.renderersParameters.boundingSphere.gpuAddress,
  1860. _DebugCounterIndex = debugCounterIndex,
  1861. _InstanceMultiplierShift = (settings.instanceMultiplier == 2) ? 1 : 0,
  1862. };
  1863. cmd.SetBufferData(m_ConstantBuffer, m_ShaderVariables);
  1864. cmd.SetComputeConstantBufferParam(cs, ShaderIDs.InstanceOcclusionCullerShaderVariables, m_ConstantBuffer, 0, m_ConstantBuffer.stride);
  1865. occlusionCullingCommon.PrepareCulling(cmd, in occluderCtx, settings, subviewSettings, m_OcclusionTestShader, occlusionDebug);
  1866. if (doCopyInstances)
  1867. {
  1868. int kernel = m_CopyInstancesKernel;
  1869. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._DrawInfo, m_IndirectStorage.drawInfoBuffer);
  1870. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._InstanceInfo, m_IndirectStorage.instanceInfoBuffer);
  1871. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._DrawArgs, m_IndirectStorage.argsBuffer);
  1872. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._InstanceIndices, m_IndirectStorage.instanceBuffer);
  1873. cmd.DispatchCompute(cs, kernel, (allocInfo.instanceCount + 63) / 64, 1, 1);
  1874. }
  1875. if (doResetDraws)
  1876. {
  1877. int kernel = m_ResetDrawArgsKernel;
  1878. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._DrawInfo, bufferHandles.drawInfoBuffer);
  1879. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._DrawArgs, bufferHandles.argsBuffer);
  1880. cmd.DispatchCompute(cs, kernel, (allocInfo.drawCount + 63) / 64, 1, 1);
  1881. }
  1882. if (doCullInstances)
  1883. {
  1884. int kernel = m_CullInstancesKernel;
  1885. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._DrawInfo, bufferHandles.drawInfoBuffer);
  1886. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._InstanceInfo, bufferHandles.instanceInfoBuffer);
  1887. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._DrawArgs, bufferHandles.argsBuffer);
  1888. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._InstanceIndices, bufferHandles.instanceBuffer);
  1889. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._InstanceDataBuffer, batchersContext.gpuInstanceDataBuffer);
  1890. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._OcclusionDebugCounters, m_OcclusionEventDebugArray.CounterBuffer);
  1891. if (isFirstPass || isSecondPass)
  1892. OcclusionCullingCommon.SetDepthPyramid(cmd, m_OcclusionTestShader, kernel, occluderHandles);
  1893. if (occlusionDebug)
  1894. OcclusionCullingCommon.SetDebugPyramid(cmd, m_OcclusionTestShader, kernel, occluderHandles);
  1895. if (isSecondPass)
  1896. cmd.DispatchCompute(cs, kernel, bufferHandles.argsBuffer, (uint)(GraphicsBuffer.IndirectDrawIndexedArgs.size * allocInfo.GetExtraDrawInfoSlotIndex()));
  1897. else
  1898. cmd.DispatchCompute(cs, kernel, (allocInfo.instanceCount + 63) / 64, 1, 1);
  1899. }
  1900. }
  1901. }
  1902. // update to the new buffer state
  1903. m_IndirectStorage.SetBufferContext(indirectContextIndex, bufferCtx);
  1904. }
  1905. }
  1906. private void FlushDebugCounters()
  1907. {
  1908. if (m_DebugStats?.enabled ?? false)
  1909. {
  1910. m_SplitDebugArray.MoveToDebugStatsAndClear(m_DebugStats);
  1911. m_OcclusionEventDebugArray.MoveToDebugStatsAndClear(m_DebugStats);
  1912. }
  1913. }
  1914. private void OnBeginSceneViewCameraRendering()
  1915. {
  1916. #if UNITY_EDITOR
  1917. m_IsSceneViewCamera = true;
  1918. #endif
  1919. }
  1920. private void OnEndSceneViewCameraRendering()
  1921. {
  1922. #if UNITY_EDITOR
  1923. m_IsSceneViewCamera = false;
  1924. #endif
  1925. }
  1926. public void UpdateFrame()
  1927. {
  1928. DisposeSceneViewHiddenBits();
  1929. DisposeCompactVisibilityMasks();
  1930. FlushDebugCounters();
  1931. m_IndirectStorage.ClearContextsAndGrowBuffers();
  1932. }
  1933. public void OnBeginCameraRendering(Camera camera)
  1934. {
  1935. if (camera.cameraType == CameraType.SceneView)
  1936. OnBeginSceneViewCameraRendering();
  1937. }
  1938. public void OnEndCameraRendering(Camera camera)
  1939. {
  1940. if (camera.cameraType == CameraType.SceneView)
  1941. OnEndSceneViewCameraRendering();
  1942. }
  1943. public void Dispose()
  1944. {
  1945. DisposeSceneViewHiddenBits();
  1946. DisposeCompactVisibilityMasks();
  1947. m_IndirectStorage.Dispose();
  1948. m_DebugStats = null;
  1949. m_OcclusionEventDebugArray.Dispose();
  1950. m_SplitDebugArray.Dispose();
  1951. m_ShaderVariables.Dispose();
  1952. m_ConstantBuffer.Release();
  1953. m_CommandBuffer.Dispose();
  1954. }
  1955. }
  1956. }