TextureGroup.cs 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611
  1. using Ryujinx.Common.Memory;
  2. using Ryujinx.Cpu.Tracking;
  3. using Ryujinx.Graphics.GAL;
  4. using Ryujinx.Graphics.Gpu.Memory;
  5. using Ryujinx.Graphics.Texture;
  6. using Ryujinx.Memory;
  7. using Ryujinx.Memory.Range;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Runtime.CompilerServices;
  11. namespace Ryujinx.Graphics.Gpu.Image
  12. {
  13. /// <summary>
  14. /// An overlapping texture group with a given view compatibility.
  15. /// </summary>
  16. readonly struct TextureIncompatibleOverlap
  17. {
  18. public readonly TextureGroup Group;
  19. public readonly TextureViewCompatibility Compatibility;
  20. /// <summary>
  21. /// Create a new texture incompatible overlap.
  22. /// </summary>
  23. /// <param name="group">The group that is incompatible</param>
  24. /// <param name="compatibility">The view compatibility for the group</param>
  25. public TextureIncompatibleOverlap(TextureGroup group, TextureViewCompatibility compatibility)
  26. {
  27. Group = group;
  28. Compatibility = compatibility;
  29. }
  30. }
  31. /// <summary>
  32. /// A texture group represents a group of textures that belong to the same storage.
  33. /// When views are created, this class will track memory accesses for them separately.
  34. /// The group iteratively adds more granular tracking as views of different kinds are added.
  35. /// Note that a texture group can be absorbed into another when it becomes a view parent.
  36. /// </summary>
  37. class TextureGroup : IDisposable
  38. {
  39. /// <summary>
  40. /// Threshold of layers to force granular handles (and thus partial loading) on array/3D textures.
  41. /// </summary>
  42. private const int GranularLayerThreshold = 8;
  43. private delegate void HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false);
  44. /// <summary>
  45. /// The storage texture associated with this group.
  46. /// </summary>
  47. public Texture Storage { get; }
  48. /// <summary>
  49. /// Indicates if the texture has copy dependencies. If true, then all modifications
  50. /// must be signalled to the group, rather than skipping ones still to be flushed.
  51. /// </summary>
  52. public bool HasCopyDependencies { get; set; }
  53. /// <summary>
  54. /// Indicates if this texture has any incompatible overlaps alive.
  55. /// </summary>
  56. public bool HasIncompatibleOverlaps => _incompatibleOverlaps.Count > 0;
  57. private readonly GpuContext _context;
  58. private readonly PhysicalMemory _physicalMemory;
  59. private int[] _allOffsets;
  60. private int[] _sliceSizes;
  61. private bool _is3D;
  62. private bool _hasMipViews;
  63. private bool _hasLayerViews;
  64. private int _layers;
  65. private int _levels;
  66. private MultiRange TextureRange => Storage.Range;
  67. /// <summary>
  68. /// The views list from the storage texture.
  69. /// </summary>
  70. private List<Texture> _views;
  71. private TextureGroupHandle[] _handles;
  72. private bool[] _loadNeeded;
  73. /// <summary>
  74. /// Other texture groups that have incompatible overlaps with this one.
  75. /// </summary>
  76. private List<TextureIncompatibleOverlap> _incompatibleOverlaps;
  77. private bool _incompatibleOverlapsDirty = true;
  78. private bool _flushIncompatibleOverlaps;
  79. /// <summary>
  80. /// Create a new texture group.
  81. /// </summary>
  82. /// <param name="context">GPU context that the texture group belongs to</param>
  83. /// <param name="physicalMemory">Physical memory where the <paramref name="storage"/> texture is mapped</param>
  84. /// <param name="storage">The storage texture for this group</param>
  85. /// <param name="incompatibleOverlaps">Groups that overlap with this one but are incompatible</param>
  86. public TextureGroup(GpuContext context, PhysicalMemory physicalMemory, Texture storage, List<TextureIncompatibleOverlap> incompatibleOverlaps)
  87. {
  88. Storage = storage;
  89. _context = context;
  90. _physicalMemory = physicalMemory;
  91. _is3D = storage.Info.Target == Target.Texture3D;
  92. _layers = storage.Info.GetSlices();
  93. _levels = storage.Info.Levels;
  94. _incompatibleOverlaps = incompatibleOverlaps;
  95. _flushIncompatibleOverlaps = TextureCompatibility.IsFormatHostIncompatible(storage.Info, context.Capabilities);
  96. }
  97. /// <summary>
  98. /// Initialize a new texture group's dirty regions and offsets.
  99. /// </summary>
  100. /// <param name="size">Size info for the storage texture</param>
  101. /// <param name="hasLayerViews">True if the storage will have layer views</param>
  102. /// <param name="hasMipViews">True if the storage will have mip views</param>
  103. public void Initialize(ref SizeInfo size, bool hasLayerViews, bool hasMipViews)
  104. {
  105. _allOffsets = size.AllOffsets;
  106. _sliceSizes = size.SliceSizes;
  107. if (Storage.Target.HasDepthOrLayers() && Storage.Info.GetSlices() > GranularLayerThreshold)
  108. {
  109. _hasLayerViews = true;
  110. _hasMipViews = true;
  111. }
  112. else
  113. {
  114. (_hasLayerViews, _hasMipViews) = PropagateGranularity(hasLayerViews, hasMipViews);
  115. // If the texture is partially mapped, fully subdivide handles immediately.
  116. MultiRange range = Storage.Range;
  117. for (int i = 0; i < range.Count; i++)
  118. {
  119. if (range.GetSubRange(i).Address == MemoryManager.PteUnmapped)
  120. {
  121. _hasLayerViews = true;
  122. _hasMipViews = true;
  123. break;
  124. }
  125. }
  126. }
  127. RecalculateHandleRegions();
  128. }
  129. /// <summary>
  130. /// Initialize all incompatible overlaps in the list, registering them with the other texture groups
  131. /// and creating copy dependencies when partially compatible.
  132. /// </summary>
  133. public void InitializeOverlaps()
  134. {
  135. foreach (TextureIncompatibleOverlap overlap in _incompatibleOverlaps)
  136. {
  137. if (overlap.Compatibility == TextureViewCompatibility.LayoutIncompatible)
  138. {
  139. CreateCopyDependency(overlap.Group, false);
  140. }
  141. overlap.Group._incompatibleOverlaps.Add(new TextureIncompatibleOverlap(this, overlap.Compatibility));
  142. overlap.Group._incompatibleOverlapsDirty = true;
  143. }
  144. if (_incompatibleOverlaps.Count > 0)
  145. {
  146. SignalIncompatibleOverlapModified();
  147. }
  148. }
  149. /// <summary>
  150. /// Signal that the group is dirty to all views and the storage.
  151. /// </summary>
  152. private void SignalAllDirty()
  153. {
  154. Storage.SignalGroupDirty();
  155. if (_views != null)
  156. {
  157. foreach (Texture texture in _views)
  158. {
  159. texture.SignalGroupDirty();
  160. }
  161. }
  162. }
  163. /// <summary>
  164. /// Signal that an incompatible overlap has been modified.
  165. /// If this group must flush incompatible overlaps, the group is signalled as dirty too.
  166. /// </summary>
  167. private void SignalIncompatibleOverlapModified()
  168. {
  169. _incompatibleOverlapsDirty = true;
  170. if (_flushIncompatibleOverlaps)
  171. {
  172. SignalAllDirty();
  173. }
  174. }
  175. /// <summary>
  176. /// Flushes incompatible overlaps if the storage format requires it, and they have been modified.
  177. /// This allows unsupported host formats to accept data written to format aliased textures.
  178. /// </summary>
  179. /// <returns>True if data was flushed, false otherwise</returns>
  180. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  181. public bool FlushIncompatibleOverlapsIfNeeded()
  182. {
  183. if (_flushIncompatibleOverlaps && _incompatibleOverlapsDirty)
  184. {
  185. bool flushed = false;
  186. foreach (var overlap in _incompatibleOverlaps)
  187. {
  188. flushed |= overlap.Group.Storage.FlushModified(true);
  189. }
  190. _incompatibleOverlapsDirty = false;
  191. return flushed;
  192. }
  193. else
  194. {
  195. return false;
  196. }
  197. }
  198. /// <summary>
  199. /// Check and optionally consume the dirty flags for a given texture.
  200. /// The state is shared between views of the same layers and levels.
  201. /// </summary>
  202. /// <param name="texture">The texture being used</param>
  203. /// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param>
  204. /// <returns>True if a flag was dirty, false otherwise</returns>
  205. public bool CheckDirty(Texture texture, bool consume)
  206. {
  207. bool dirty = false;
  208. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  209. {
  210. for (int i = 0; i < regionCount; i++)
  211. {
  212. TextureGroupHandle group = _handles[baseHandle + i];
  213. foreach (CpuRegionHandle handle in group.Handles)
  214. {
  215. if (handle.Dirty)
  216. {
  217. if (consume)
  218. {
  219. handle.Reprotect();
  220. }
  221. dirty = true;
  222. }
  223. }
  224. }
  225. });
  226. return dirty;
  227. }
  228. /// <summary>
  229. /// Synchronize memory for a given texture.
  230. /// If overlapping tracking handles are dirty, fully or partially synchronize the texture data.
  231. /// </summary>
  232. /// <param name="texture">The texture being used</param>
  233. public void SynchronizeMemory(Texture texture)
  234. {
  235. FlushIncompatibleOverlapsIfNeeded();
  236. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  237. {
  238. bool dirty = false;
  239. bool anyModified = false;
  240. bool anyNotDirty = false;
  241. for (int i = 0; i < regionCount; i++)
  242. {
  243. TextureGroupHandle group = _handles[baseHandle + i];
  244. bool modified = group.Modified;
  245. bool handleDirty = false;
  246. bool handleUnmapped = false;
  247. foreach (CpuRegionHandle handle in group.Handles)
  248. {
  249. if (handle.Dirty)
  250. {
  251. handle.Reprotect();
  252. handleDirty = true;
  253. }
  254. else
  255. {
  256. handleUnmapped |= handle.Unmapped;
  257. }
  258. }
  259. // If the modified flag is still present, prefer the data written from gpu.
  260. // A write from CPU will do a flush before writing its data, which should unset this.
  261. if (modified)
  262. {
  263. handleDirty = false;
  264. }
  265. // Evaluate if any copy dependencies need to be fulfilled. A few rules:
  266. // If the copy handle needs to be synchronized, prefer our own state.
  267. // If we need to be synchronized and there is a copy present, prefer the copy.
  268. if (group.NeedsCopy && group.Copy(_context))
  269. {
  270. anyModified |= true; // The copy target has been modified.
  271. handleDirty = false;
  272. }
  273. else
  274. {
  275. anyModified |= modified;
  276. dirty |= handleDirty;
  277. }
  278. if (group.NeedsCopy)
  279. {
  280. // The texture we copied from is still being written to. Copy from it again the next time this texture is used.
  281. texture.SignalGroupDirty();
  282. }
  283. bool loadNeeded = handleDirty && !handleUnmapped;
  284. anyNotDirty |= !loadNeeded;
  285. _loadNeeded[baseHandle + i] = loadNeeded;
  286. }
  287. if (dirty)
  288. {
  289. if (anyNotDirty || (_handles.Length > 1 && (anyModified || split)))
  290. {
  291. // Partial texture invalidation. Only update the layers/levels with dirty flags of the storage.
  292. SynchronizePartial(baseHandle, regionCount);
  293. }
  294. else
  295. {
  296. // Full texture invalidation.
  297. texture.SynchronizeFull();
  298. }
  299. }
  300. });
  301. }
  302. /// <summary>
  303. /// Synchronize part of the storage texture, represented by a given range of handles.
  304. /// Only handles marked by the _loadNeeded array will be synchronized.
  305. /// </summary>
  306. /// <param name="baseHandle">The base index of the range of handles</param>
  307. /// <param name="regionCount">The number of handles to synchronize</param>
  308. private void SynchronizePartial(int baseHandle, int regionCount)
  309. {
  310. int spanEndIndex = -1;
  311. int spanBase = 0;
  312. ReadOnlySpan<byte> dataSpan = ReadOnlySpan<byte>.Empty;
  313. for (int i = 0; i < regionCount; i++)
  314. {
  315. if (_loadNeeded[baseHandle + i])
  316. {
  317. var info = GetHandleInformation(baseHandle + i);
  318. // Ensure the data for this handle is loaded in the span.
  319. if (spanEndIndex <= i - 1)
  320. {
  321. spanEndIndex = i;
  322. if (_is3D)
  323. {
  324. // Look ahead to see how many handles need to be loaded.
  325. for (int j = i + 1; j < regionCount; j++)
  326. {
  327. if (_loadNeeded[baseHandle + j])
  328. {
  329. spanEndIndex = j;
  330. }
  331. else
  332. {
  333. break;
  334. }
  335. }
  336. }
  337. var endInfo = spanEndIndex == i ? info : GetHandleInformation(baseHandle + spanEndIndex);
  338. spanBase = _allOffsets[info.Index];
  339. int spanLast = _allOffsets[endInfo.Index + endInfo.Layers * endInfo.Levels - 1];
  340. int endOffset = Math.Min(spanLast + _sliceSizes[endInfo.BaseLevel + endInfo.Levels - 1], (int)Storage.Size);
  341. int size = endOffset - spanBase;
  342. dataSpan = _physicalMemory.GetSpan(Storage.Range.Slice((ulong)spanBase, (ulong)size));
  343. }
  344. // Only one of these will be greater than 1, as partial sync is only called when there are sub-image views.
  345. for (int layer = 0; layer < info.Layers; layer++)
  346. {
  347. for (int level = 0; level < info.Levels; level++)
  348. {
  349. int offsetIndex = GetOffsetIndex(info.BaseLayer + layer, info.BaseLevel + level);
  350. int offset = _allOffsets[offsetIndex];
  351. ReadOnlySpan<byte> data = dataSpan.Slice(offset - spanBase);
  352. SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
  353. Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
  354. }
  355. }
  356. }
  357. }
  358. }
  359. /// <summary>
  360. /// Synchronize dependent textures, if any of them have deferred a copy from the given texture.
  361. /// </summary>
  362. /// <param name="texture">The texture to synchronize dependents of</param>
  363. public void SynchronizeDependents(Texture texture)
  364. {
  365. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  366. {
  367. for (int i = 0; i < regionCount; i++)
  368. {
  369. TextureGroupHandle group = _handles[baseHandle + i];
  370. group.SynchronizeDependents();
  371. }
  372. });
  373. }
  374. /// <summary>
  375. /// Determines whether flushes in this texture group should be tracked.
  376. /// Incompatible overlaps may need data from this texture to flush tracked for it to be visible to them.
  377. /// </summary>
  378. /// <returns>True if flushes should be tracked, false otherwise</returns>
  379. private bool ShouldFlushTriggerTracking()
  380. {
  381. foreach (var overlap in _incompatibleOverlaps)
  382. {
  383. if (overlap.Group._flushIncompatibleOverlaps)
  384. {
  385. return true;
  386. }
  387. }
  388. return false;
  389. }
  390. /// <summary>
  391. /// Gets data from the host GPU, and flushes a slice to guest memory.
  392. /// </summary>
  393. /// <remarks>
  394. /// This method should be used to retrieve data that was modified by the host GPU.
  395. /// This is not cheap, avoid doing that unless strictly needed.
  396. /// When possible, the data is written directly into guest memory, rather than copied.
  397. /// </remarks>
  398. /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
  399. /// <param name="sliceIndex">The index of the slice to flush</param>
  400. /// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param>
  401. private void FlushTextureDataSliceToGuest(bool tracked, int sliceIndex, ITexture texture = null)
  402. {
  403. (int layer, int level) = GetLayerLevelForView(sliceIndex);
  404. int offset = _allOffsets[sliceIndex];
  405. int endOffset = Math.Min(offset + _sliceSizes[level], (int)Storage.Size);
  406. int size = endOffset - offset;
  407. using WritableRegion region = _physicalMemory.GetWritableRegion(Storage.Range.Slice((ulong)offset, (ulong)size), tracked);
  408. Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture);
  409. }
  410. /// <summary>
  411. /// Gets and flushes a number of slices of the storage texture to guest memory.
  412. /// </summary>
  413. /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
  414. /// <param name="sliceStart">The first slice to flush</param>
  415. /// <param name="sliceEnd">The slice to finish flushing on (exclusive)</param>
  416. /// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param>
  417. private void FlushSliceRange(bool tracked, int sliceStart, int sliceEnd, ITexture texture = null)
  418. {
  419. for (int i = sliceStart; i < sliceEnd; i++)
  420. {
  421. FlushTextureDataSliceToGuest(tracked, i, texture);
  422. }
  423. }
  424. /// <summary>
  425. /// Flush modified ranges for a given texture.
  426. /// </summary>
  427. /// <param name="texture">The texture being used</param>
  428. /// <param name="tracked">True if the flush writes should be tracked, false otherwise</param>
  429. /// <returns>True if data was flushed, false otherwise</returns>
  430. public bool FlushModified(Texture texture, bool tracked)
  431. {
  432. tracked = tracked || ShouldFlushTriggerTracking();
  433. bool flushed = false;
  434. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  435. {
  436. int startSlice = 0;
  437. int endSlice = 0;
  438. bool allModified = true;
  439. for (int i = 0; i < regionCount; i++)
  440. {
  441. TextureGroupHandle group = _handles[baseHandle + i];
  442. if (group.Modified)
  443. {
  444. if (endSlice < group.BaseSlice)
  445. {
  446. if (endSlice > startSlice)
  447. {
  448. FlushSliceRange(tracked, startSlice, endSlice);
  449. flushed = true;
  450. }
  451. startSlice = group.BaseSlice;
  452. }
  453. endSlice = group.BaseSlice + group.SliceCount;
  454. if (tracked)
  455. {
  456. group.Modified = false;
  457. foreach (Texture texture in group.Overlaps)
  458. {
  459. texture.SignalModifiedDirty();
  460. }
  461. }
  462. }
  463. else
  464. {
  465. allModified = false;
  466. }
  467. }
  468. if (endSlice > startSlice)
  469. {
  470. if (allModified && !split)
  471. {
  472. texture.Flush(tracked);
  473. }
  474. else
  475. {
  476. FlushSliceRange(tracked, startSlice, endSlice);
  477. }
  478. flushed = true;
  479. }
  480. });
  481. Storage.SignalModifiedDirty();
  482. return flushed;
  483. }
  484. /// <summary>
  485. /// Clears competing modified flags for all incompatible ranges, if they have possibly been modified.
  486. /// </summary>
  487. /// <param name="texture">The texture that has been modified</param>
  488. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  489. private void ClearIncompatibleOverlaps(Texture texture)
  490. {
  491. if (_incompatibleOverlapsDirty)
  492. {
  493. foreach (TextureIncompatibleOverlap incompatible in _incompatibleOverlaps)
  494. {
  495. incompatible.Group.ClearModified(texture.Range, this);
  496. incompatible.Group.SignalIncompatibleOverlapModified();
  497. }
  498. _incompatibleOverlapsDirty = false;
  499. }
  500. }
  501. /// <summary>
  502. /// Signal that a texture in the group has been modified by the GPU.
  503. /// </summary>
  504. /// <param name="texture">The texture that has been modified</param>
  505. public void SignalModified(Texture texture)
  506. {
  507. ClearIncompatibleOverlaps(texture);
  508. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  509. {
  510. for (int i = 0; i < regionCount; i++)
  511. {
  512. TextureGroupHandle group = _handles[baseHandle + i];
  513. group.SignalModified(_context);
  514. }
  515. });
  516. }
  517. /// <summary>
  518. /// Signal that a texture in the group is actively bound, or has been unbound by the GPU.
  519. /// </summary>
  520. /// <param name="texture">The texture that has been modified</param>
  521. /// <param name="bound">True if this texture is being bound, false if unbound</param>
  522. public void SignalModifying(Texture texture, bool bound)
  523. {
  524. ClearIncompatibleOverlaps(texture);
  525. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  526. {
  527. for (int i = 0; i < regionCount; i++)
  528. {
  529. TextureGroupHandle group = _handles[baseHandle + i];
  530. group.SignalModifying(bound, _context);
  531. }
  532. });
  533. }
  534. /// <summary>
  535. /// Register a read/write action to flush for a texture group.
  536. /// </summary>
  537. /// <param name="group">The group to register an action for</param>
  538. public void RegisterAction(TextureGroupHandle group)
  539. {
  540. foreach (CpuRegionHandle handle in group.Handles)
  541. {
  542. handle.RegisterAction((address, size) => FlushAction(group, address, size));
  543. }
  544. }
  545. /// <summary>
  546. /// Propagates the mip/layer view flags depending on the texture type.
  547. /// When the most granular type of subresource has views, the other type of subresource must be segmented granularly too.
  548. /// </summary>
  549. /// <param name="hasLayerViews">True if the storage has layer views</param>
  550. /// <param name="hasMipViews">True if the storage has mip views</param>
  551. /// <returns>The input values after propagation</returns>
  552. private (bool HasLayerViews, bool HasMipViews) PropagateGranularity(bool hasLayerViews, bool hasMipViews)
  553. {
  554. if (_is3D)
  555. {
  556. hasMipViews |= hasLayerViews;
  557. }
  558. else
  559. {
  560. hasLayerViews |= hasMipViews;
  561. }
  562. return (hasLayerViews, hasMipViews);
  563. }
  564. /// <summary>
  565. /// Evaluate the range of tracking handles which a view texture overlaps with.
  566. /// </summary>
  567. /// <param name="texture">The texture to get handles for</param>
  568. /// <param name="callback">
  569. /// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
  570. /// This can be called for multiple disjoint ranges, if required.
  571. /// </param>
  572. private void EvaluateRelevantHandles(Texture texture, HandlesCallbackDelegate callback)
  573. {
  574. if (texture == Storage || !(_hasMipViews || _hasLayerViews))
  575. {
  576. callback(0, _handles.Length);
  577. return;
  578. }
  579. EvaluateRelevantHandles(texture.FirstLayer, texture.FirstLevel, texture.Info.GetSlices(), texture.Info.Levels, callback);
  580. }
  581. /// <summary>
  582. /// Evaluate the range of tracking handles which a view texture overlaps with,
  583. /// using the view's position and slice/level counts.
  584. /// </summary>
  585. /// <param name="firstLayer">The first layer of the texture</param>
  586. /// <param name="firstLevel">The first level of the texture</param>
  587. /// <param name="slices">The slice count of the texture</param>
  588. /// <param name="levels">The level count of the texture</param>
  589. /// <param name="callback">
  590. /// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
  591. /// This can be called for multiple disjoint ranges, if required.
  592. /// </param>
  593. private void EvaluateRelevantHandles(int firstLayer, int firstLevel, int slices, int levels, HandlesCallbackDelegate callback)
  594. {
  595. int targetLayerHandles = _hasLayerViews ? slices : 1;
  596. int targetLevelHandles = _hasMipViews ? levels : 1;
  597. if (_is3D)
  598. {
  599. // Future mip levels come after all layers of the last mip level. Each mipmap has less layers (depth) than the last.
  600. if (!_hasLayerViews)
  601. {
  602. // When there are no layer views, the mips are at a consistent offset.
  603. callback(firstLevel, targetLevelHandles);
  604. }
  605. else
  606. {
  607. (int levelIndex, int layerCount) = Get3DLevelRange(firstLevel);
  608. if (levels > 1 && slices < _layers)
  609. {
  610. // The given texture only covers some of the depth of multiple mips. (a "depth slice")
  611. // Callback with each mip's range separately.
  612. // Can assume that the group is fully subdivided (both slices and levels > 1 for storage)
  613. while (levels-- > 1)
  614. {
  615. callback(firstLayer + levelIndex, slices);
  616. levelIndex += layerCount;
  617. layerCount = Math.Max(layerCount >> 1, 1);
  618. slices = Math.Max(layerCount >> 1, 1);
  619. }
  620. }
  621. else
  622. {
  623. int totalSize = Math.Min(layerCount, slices);
  624. while (levels-- > 1)
  625. {
  626. layerCount = Math.Max(layerCount >> 1, 1);
  627. totalSize += layerCount;
  628. }
  629. callback(firstLayer + levelIndex, totalSize);
  630. }
  631. }
  632. }
  633. else
  634. {
  635. // Future layers come after all mipmaps of the last.
  636. int levelHandles = _hasMipViews ? _levels : 1;
  637. if (slices > 1 && levels < _levels)
  638. {
  639. // The given texture only covers some of the mipmaps of multiple slices. (a "mip slice")
  640. // Callback with each layer's range separately.
  641. // Can assume that the group is fully subdivided (both slices and levels > 1 for storage)
  642. for (int i = 0; i < slices; i++)
  643. {
  644. callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true);
  645. }
  646. }
  647. else
  648. {
  649. callback(firstLevel + firstLayer * levelHandles, targetLevelHandles + (targetLayerHandles - 1) * levelHandles);
  650. }
  651. }
  652. }
  653. /// <summary>
  654. /// Get the range of offsets for a given mip level of a 3D texture.
  655. /// </summary>
  656. /// <param name="level">The level to return</param>
  657. /// <returns>Start index and count of offsets for the given level</returns>
  658. private (int Index, int Count) Get3DLevelRange(int level)
  659. {
  660. int index = 0;
  661. int count = _layers; // Depth. Halves with each mip level.
  662. while (level-- > 0)
  663. {
  664. index += count;
  665. count = Math.Max(count >> 1, 1);
  666. }
  667. return (index, count);
  668. }
  669. /// <summary>
  670. /// Get view information for a single tracking handle.
  671. /// </summary>
  672. /// <param name="handleIndex">The index of the handle</param>
  673. /// <returns>The layers and levels that the handle covers, and its index in the offsets array</returns>
  674. private (int BaseLayer, int BaseLevel, int Levels, int Layers, int Index) GetHandleInformation(int handleIndex)
  675. {
  676. int baseLayer;
  677. int baseLevel;
  678. int levels = _hasMipViews ? 1 : _levels;
  679. int layers = _hasLayerViews ? 1 : _layers;
  680. int index;
  681. if (_is3D)
  682. {
  683. if (_hasLayerViews)
  684. {
  685. // NOTE: Will also have mip views, or only one level in storage.
  686. index = handleIndex;
  687. baseLevel = 0;
  688. int levelLayers = _layers;
  689. while (handleIndex >= levelLayers)
  690. {
  691. handleIndex -= levelLayers;
  692. baseLevel++;
  693. levelLayers = Math.Max(levelLayers >> 1, 1);
  694. }
  695. baseLayer = handleIndex;
  696. }
  697. else
  698. {
  699. baseLayer = 0;
  700. baseLevel = handleIndex;
  701. (index, _) = Get3DLevelRange(baseLevel);
  702. }
  703. }
  704. else
  705. {
  706. baseLevel = _hasMipViews ? handleIndex % _levels : 0;
  707. baseLayer = _hasMipViews ? handleIndex / _levels : handleIndex;
  708. index = baseLevel + baseLayer * _levels;
  709. }
  710. return (baseLayer, baseLevel, levels, layers, index);
  711. }
  712. /// <summary>
  713. /// Gets the layer and level for a given view.
  714. /// </summary>
  715. /// <param name="index">The index of the view</param>
  716. /// <returns>The layer and level of the specified view</returns>
  717. private (int BaseLayer, int BaseLevel) GetLayerLevelForView(int index)
  718. {
  719. if (_is3D)
  720. {
  721. int baseLevel = 0;
  722. int levelLayers = _layers;
  723. while (index >= levelLayers)
  724. {
  725. index -= levelLayers;
  726. baseLevel++;
  727. levelLayers = Math.Max(levelLayers >> 1, 1);
  728. }
  729. return (index, baseLevel);
  730. }
  731. else
  732. {
  733. return (index / _levels, index % _levels);
  734. }
  735. }
  736. /// <summary>
  737. /// Find the byte offset of a given texture relative to the storage.
  738. /// </summary>
  739. /// <param name="texture">The texture to locate</param>
  740. /// <returns>The offset of the texture in bytes</returns>
  741. public int FindOffset(Texture texture)
  742. {
  743. return _allOffsets[GetOffsetIndex(texture.FirstLayer, texture.FirstLevel)];
  744. }
  745. /// <summary>
  746. /// Find the offset index of a given layer and level.
  747. /// </summary>
  748. /// <param name="layer">The view layer</param>
  749. /// <param name="level">The view level</param>
  750. /// <returns>The offset index of the given layer and level</returns>
  751. public int GetOffsetIndex(int layer, int level)
  752. {
  753. if (_is3D)
  754. {
  755. return layer + Get3DLevelRange(level).Index;
  756. }
  757. else
  758. {
  759. return level + layer * _levels;
  760. }
  761. }
  762. /// <summary>
  763. /// The action to perform when a memory tracking handle is flipped to dirty.
  764. /// This notifies overlapping textures that the memory needs to be synchronized.
  765. /// </summary>
  766. /// <param name="groupHandle">The handle that a dirty flag was set on</param>
  767. private void DirtyAction(TextureGroupHandle groupHandle)
  768. {
  769. // Notify all textures that belong to this handle.
  770. Storage.SignalGroupDirty();
  771. lock (groupHandle.Overlaps)
  772. {
  773. foreach (Texture overlap in groupHandle.Overlaps)
  774. {
  775. overlap.SignalGroupDirty();
  776. }
  777. }
  778. }
  779. /// <summary>
  780. /// Generate a CpuRegionHandle for a given address and size range in CPU VA.
  781. /// </summary>
  782. /// <param name="address">The start address of the tracked region</param>
  783. /// <param name="size">The size of the tracked region</param>
  784. /// <returns>A CpuRegionHandle covering the given range</returns>
  785. private CpuRegionHandle GenerateHandle(ulong address, ulong size)
  786. {
  787. return _physicalMemory.BeginTracking(address, size, ResourceKind.Texture);
  788. }
  789. /// <summary>
  790. /// Generate a TextureGroupHandle covering a specified range of views.
  791. /// </summary>
  792. /// <param name="viewStart">The start view of the handle</param>
  793. /// <param name="views">The number of views to cover</param>
  794. /// <returns>A TextureGroupHandle covering the given views</returns>
  795. private TextureGroupHandle GenerateHandles(int viewStart, int views)
  796. {
  797. int viewEnd = viewStart + views - 1;
  798. (_, int lastLevel) = GetLayerLevelForView(viewEnd);
  799. int offset = _allOffsets[viewStart];
  800. int endOffset = _allOffsets[viewEnd] + _sliceSizes[lastLevel];
  801. int size = endOffset - offset;
  802. var result = new List<CpuRegionHandle>();
  803. for (int i = 0; i < TextureRange.Count; i++)
  804. {
  805. MemoryRange item = TextureRange.GetSubRange(i);
  806. int subRangeSize = (int)item.Size;
  807. int sliceStart = Math.Clamp(offset, 0, subRangeSize);
  808. int sliceEnd = Math.Clamp(endOffset, 0, subRangeSize);
  809. if (sliceStart != sliceEnd && item.Address != MemoryManager.PteUnmapped)
  810. {
  811. result.Add(GenerateHandle(item.Address + (ulong)sliceStart, (ulong)(sliceEnd - sliceStart)));
  812. }
  813. offset -= subRangeSize;
  814. endOffset -= subRangeSize;
  815. if (endOffset <= 0)
  816. {
  817. break;
  818. }
  819. }
  820. (int firstLayer, int firstLevel) = GetLayerLevelForView(viewStart);
  821. if (_hasLayerViews && _hasMipViews)
  822. {
  823. size = _sliceSizes[firstLevel];
  824. }
  825. offset = _allOffsets[viewStart];
  826. ulong maxSize = Storage.Size - (ulong)offset;
  827. var groupHandle = new TextureGroupHandle(
  828. this,
  829. offset,
  830. Math.Min(maxSize, (ulong)size),
  831. _views,
  832. firstLayer,
  833. firstLevel,
  834. viewStart,
  835. views,
  836. result.ToArray());
  837. foreach (CpuRegionHandle handle in result)
  838. {
  839. handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
  840. }
  841. return groupHandle;
  842. }
  843. /// <summary>
  844. /// Update the views in this texture group, rebuilding the memory tracking if required.
  845. /// </summary>
  846. /// <param name="views">The views list of the storage texture</param>
  847. /// <param name="texture">The texture that has been added, if that is the only change, otherwise null</param>
  848. public void UpdateViews(List<Texture> views, Texture texture)
  849. {
  850. // This is saved to calculate overlapping views for each handle.
  851. _views = views;
  852. bool layerViews = _hasLayerViews;
  853. bool mipViews = _hasMipViews;
  854. bool regionsRebuilt = false;
  855. if (!(layerViews && mipViews))
  856. {
  857. foreach (Texture view in views)
  858. {
  859. if (view.Info.GetSlices() < _layers)
  860. {
  861. layerViews = true;
  862. }
  863. if (view.Info.Levels < _levels)
  864. {
  865. mipViews = true;
  866. }
  867. }
  868. (layerViews, mipViews) = PropagateGranularity(layerViews, mipViews);
  869. if (layerViews != _hasLayerViews || mipViews != _hasMipViews)
  870. {
  871. _hasLayerViews = layerViews;
  872. _hasMipViews = mipViews;
  873. RecalculateHandleRegions();
  874. regionsRebuilt = true;
  875. }
  876. }
  877. if (!regionsRebuilt)
  878. {
  879. if (texture != null)
  880. {
  881. int offset = FindOffset(texture);
  882. foreach (TextureGroupHandle handle in _handles)
  883. {
  884. handle.AddOverlap(offset, texture);
  885. }
  886. }
  887. else
  888. {
  889. // Must update the overlapping views on all handles, but only if they were not just recreated.
  890. foreach (TextureGroupHandle handle in _handles)
  891. {
  892. handle.RecalculateOverlaps(this, views);
  893. }
  894. }
  895. }
  896. SignalAllDirty();
  897. }
  898. /// <summary>
  899. /// Removes a view from the group, removing it from all overlap lists.
  900. /// </summary>
  901. /// <param name="view">View to remove from the group</param>
  902. public void RemoveView(Texture view)
  903. {
  904. int offset = FindOffset(view);
  905. foreach (TextureGroupHandle handle in _handles)
  906. {
  907. handle.RemoveOverlap(offset, view);
  908. }
  909. }
  910. /// <summary>
  911. /// Inherit handle state from an old set of handles, such as modified and dirty flags.
  912. /// </summary>
  913. /// <param name="oldHandles">The set of handles to inherit state from</param>
  914. /// <param name="handles">The set of handles inheriting the state</param>
  915. /// <param name="relativeOffset">The offset of the old handles in relation to the new ones</param>
  916. private void InheritHandles(TextureGroupHandle[] oldHandles, TextureGroupHandle[] handles, int relativeOffset)
  917. {
  918. foreach (var group in handles)
  919. {
  920. foreach (var handle in group.Handles)
  921. {
  922. bool dirty = false;
  923. foreach (var oldGroup in oldHandles)
  924. {
  925. if (group.OverlapsWith(oldGroup.Offset + relativeOffset, oldGroup.Size))
  926. {
  927. foreach (var oldHandle in oldGroup.Handles)
  928. {
  929. if (handle.OverlapsWith(oldHandle.Address, oldHandle.Size))
  930. {
  931. dirty |= oldHandle.Dirty;
  932. }
  933. }
  934. group.Inherit(oldGroup, group.Offset == oldGroup.Offset + relativeOffset);
  935. }
  936. }
  937. if (dirty && !handle.Dirty)
  938. {
  939. handle.Reprotect(true);
  940. }
  941. if (group.Modified)
  942. {
  943. handle.RegisterAction((address, size) => FlushAction(group, address, size));
  944. }
  945. }
  946. }
  947. foreach (var oldGroup in oldHandles)
  948. {
  949. oldGroup.Modified = false;
  950. }
  951. }
  952. /// <summary>
  953. /// Inherit state from another texture group.
  954. /// </summary>
  955. /// <param name="other">The texture group to inherit from</param>
  956. public void Inherit(TextureGroup other)
  957. {
  958. bool layerViews = _hasLayerViews || other._hasLayerViews;
  959. bool mipViews = _hasMipViews || other._hasMipViews;
  960. if (layerViews != _hasLayerViews || mipViews != _hasMipViews)
  961. {
  962. _hasLayerViews = layerViews;
  963. _hasMipViews = mipViews;
  964. RecalculateHandleRegions();
  965. }
  966. foreach (TextureIncompatibleOverlap incompatible in other._incompatibleOverlaps)
  967. {
  968. RegisterIncompatibleOverlap(incompatible, false);
  969. incompatible.Group._incompatibleOverlaps.RemoveAll(overlap => overlap.Group == other);
  970. }
  971. int relativeOffset = Storage.Range.FindOffset(other.Storage.Range);
  972. InheritHandles(other._handles, _handles, relativeOffset);
  973. }
  974. /// <summary>
  975. /// Replace the current handles with the new handles. It is assumed that the new handles start dirty.
  976. /// The dirty flags from the previous handles will be kept.
  977. /// </summary>
  978. /// <param name="handles">The handles to replace the current handles with</param>
  979. /// <param name="rangeChanged">True if the storage memory range changed since the last region handle generation</param>
  980. private void ReplaceHandles(TextureGroupHandle[] handles, bool rangeChanged)
  981. {
  982. if (_handles != null)
  983. {
  984. // When replacing handles, they should start as non-dirty.
  985. foreach (TextureGroupHandle groupHandle in handles)
  986. {
  987. if (rangeChanged)
  988. {
  989. // When the storage range changes, this becomes a little different.
  990. // If a range does not match one in the original, treat it as modified.
  991. // It has been newly mapped and its data must be synchronized.
  992. if (groupHandle.Handles.Length == 0)
  993. {
  994. continue;
  995. }
  996. foreach (var oldGroup in _handles)
  997. {
  998. if (!groupHandle.OverlapsWith(oldGroup.Offset, oldGroup.Size))
  999. {
  1000. continue;
  1001. }
  1002. foreach (CpuRegionHandle handle in groupHandle.Handles)
  1003. {
  1004. bool hasMatch = false;
  1005. foreach (var oldHandle in oldGroup.Handles)
  1006. {
  1007. if (oldHandle.RangeEquals(handle))
  1008. {
  1009. hasMatch = true;
  1010. break;
  1011. }
  1012. }
  1013. if (hasMatch)
  1014. {
  1015. handle.Reprotect();
  1016. }
  1017. }
  1018. }
  1019. }
  1020. else
  1021. {
  1022. foreach (CpuRegionHandle handle in groupHandle.Handles)
  1023. {
  1024. handle.Reprotect();
  1025. }
  1026. }
  1027. }
  1028. InheritHandles(_handles, handles, 0);
  1029. foreach (var oldGroup in _handles)
  1030. {
  1031. foreach (var oldHandle in oldGroup.Handles)
  1032. {
  1033. oldHandle.Dispose();
  1034. }
  1035. }
  1036. }
  1037. _handles = handles;
  1038. _loadNeeded = new bool[_handles.Length];
  1039. }
  1040. /// <summary>
  1041. /// Recalculate handle regions for this texture group, and inherit existing state into the new handles.
  1042. /// </summary>
  1043. /// <param name="rangeChanged">True if the storage memory range changed since the last region handle generation</param>
  1044. private void RecalculateHandleRegions(bool rangeChanged = false)
  1045. {
  1046. TextureGroupHandle[] handles;
  1047. if (!(_hasMipViews || _hasLayerViews))
  1048. {
  1049. // Single dirty region.
  1050. var cpuRegionHandles = new CpuRegionHandle[TextureRange.Count];
  1051. int count = 0;
  1052. for (int i = 0; i < TextureRange.Count; i++)
  1053. {
  1054. var currentRange = TextureRange.GetSubRange(i);
  1055. if (currentRange.Address != MemoryManager.PteUnmapped)
  1056. {
  1057. cpuRegionHandles[count++] = GenerateHandle(currentRange.Address, currentRange.Size);
  1058. }
  1059. }
  1060. if (count != TextureRange.Count)
  1061. {
  1062. Array.Resize(ref cpuRegionHandles, count);
  1063. }
  1064. var groupHandle = new TextureGroupHandle(this, 0, Storage.Size, _views, 0, 0, 0, _allOffsets.Length, cpuRegionHandles);
  1065. foreach (CpuRegionHandle handle in cpuRegionHandles)
  1066. {
  1067. handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
  1068. }
  1069. handles = new TextureGroupHandle[] { groupHandle };
  1070. }
  1071. else
  1072. {
  1073. // Get views for the host texture.
  1074. // It's worth noting that either the texture has layer views or mip views when getting to this point, which simplifies the logic a little.
  1075. // Depending on if the texture is 3d, either the mip views imply that layer views are present (2d) or the other way around (3d).
  1076. // This is enforced by the way the texture matched as a view, so we don't need to check.
  1077. int layerHandles = _hasLayerViews ? _layers : 1;
  1078. int levelHandles = _hasMipViews ? _levels : 1;
  1079. int handleIndex = 0;
  1080. if (_is3D)
  1081. {
  1082. var handlesList = new List<TextureGroupHandle>();
  1083. for (int i = 0; i < levelHandles; i++)
  1084. {
  1085. for (int j = 0; j < layerHandles; j++)
  1086. {
  1087. (int viewStart, int views) = Get3DLevelRange(i);
  1088. viewStart += j;
  1089. views = _hasLayerViews ? 1 : views; // A layer view is also a mip view.
  1090. handlesList.Add(GenerateHandles(viewStart, views));
  1091. }
  1092. layerHandles = Math.Max(1, layerHandles >> 1);
  1093. }
  1094. handles = handlesList.ToArray();
  1095. }
  1096. else
  1097. {
  1098. handles = new TextureGroupHandle[layerHandles * levelHandles];
  1099. for (int i = 0; i < layerHandles; i++)
  1100. {
  1101. for (int j = 0; j < levelHandles; j++)
  1102. {
  1103. int viewStart = j + i * _levels;
  1104. int views = _hasMipViews ? 1 : _levels; // A mip view is also a layer view.
  1105. handles[handleIndex++] = GenerateHandles(viewStart, views);
  1106. }
  1107. }
  1108. }
  1109. }
  1110. ReplaceHandles(handles, rangeChanged);
  1111. }
  1112. /// <summary>
  1113. /// Regenerates handles when the storage range has been remapped.
  1114. /// This forces the regions to be fully subdivided.
  1115. /// </summary>
  1116. public void RangeChanged()
  1117. {
  1118. _hasLayerViews = true;
  1119. _hasMipViews = true;
  1120. RecalculateHandleRegions(true);
  1121. SignalAllDirty();
  1122. }
  1123. /// <summary>
  1124. /// Ensure that there is a handle for each potential texture view. Required for copy dependencies to work.
  1125. /// </summary>
  1126. private void EnsureFullSubdivision()
  1127. {
  1128. if (!(_hasLayerViews && _hasMipViews))
  1129. {
  1130. _hasLayerViews = true;
  1131. _hasMipViews = true;
  1132. RecalculateHandleRegions();
  1133. }
  1134. }
  1135. /// <summary>
  1136. /// Create a copy dependency between this texture group, and a texture at a given layer/level offset.
  1137. /// </summary>
  1138. /// <param name="other">The view compatible texture to create a dependency to</param>
  1139. /// <param name="firstLayer">The base layer of the given texture relative to the storage</param>
  1140. /// <param name="firstLevel">The base level of the given texture relative to the storage</param>
  1141. /// <param name="copyTo">True if this texture is first copied to the given one, false for the opposite direction</param>
  1142. public void CreateCopyDependency(Texture other, int firstLayer, int firstLevel, bool copyTo)
  1143. {
  1144. TextureGroup otherGroup = other.Group;
  1145. EnsureFullSubdivision();
  1146. otherGroup.EnsureFullSubdivision();
  1147. // Get the location of each texture within its storage, so we can find the handles to apply the dependency to.
  1148. // This can consist of multiple disjoint regions, for example if this is a mip slice of an array texture.
  1149. var targetRange = new List<(int BaseHandle, int RegionCount)>();
  1150. var otherRange = new List<(int BaseHandle, int RegionCount)>();
  1151. EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, split) => targetRange.Add((baseHandle, regionCount)));
  1152. otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, split) => otherRange.Add((baseHandle, regionCount)));
  1153. int targetIndex = 0;
  1154. int otherIndex = 0;
  1155. (int Handle, int RegionCount) targetRegion = (0, 0);
  1156. (int Handle, int RegionCount) otherRegion = (0, 0);
  1157. while (true)
  1158. {
  1159. if (targetRegion.RegionCount == 0)
  1160. {
  1161. if (targetIndex >= targetRange.Count)
  1162. {
  1163. break;
  1164. }
  1165. targetRegion = targetRange[targetIndex++];
  1166. }
  1167. if (otherRegion.RegionCount == 0)
  1168. {
  1169. if (otherIndex >= otherRange.Count)
  1170. {
  1171. break;
  1172. }
  1173. otherRegion = otherRange[otherIndex++];
  1174. }
  1175. TextureGroupHandle handle = _handles[targetRegion.Handle++];
  1176. TextureGroupHandle otherHandle = other.Group._handles[otherRegion.Handle++];
  1177. targetRegion.RegionCount--;
  1178. otherRegion.RegionCount--;
  1179. handle.CreateCopyDependency(otherHandle, copyTo);
  1180. // If "copyTo" is true, this texture must copy to the other.
  1181. // Otherwise, it must copy to this texture.
  1182. if (copyTo)
  1183. {
  1184. otherHandle.Copy(_context, handle);
  1185. }
  1186. else
  1187. {
  1188. handle.Copy(_context, otherHandle);
  1189. }
  1190. }
  1191. }
  1192. /// <summary>
  1193. /// Creates a copy dependency to another texture group, where handles overlap.
  1194. /// Scans through all handles to find compatible patches in the other group.
  1195. /// </summary>
  1196. /// <param name="other">The texture group that overlaps this one</param>
  1197. /// <param name="copyTo">True if this texture is first copied to the given one, false for the opposite direction</param>
  1198. public void CreateCopyDependency(TextureGroup other, bool copyTo)
  1199. {
  1200. for (int i = 0; i < _allOffsets.Length; i++)
  1201. {
  1202. (int layer, int level) = GetLayerLevelForView(i);
  1203. MultiRange handleRange = Storage.Range.Slice((ulong)_allOffsets[i], 1);
  1204. ulong handleBase = handleRange.GetSubRange(0).Address;
  1205. for (int j = 0; j < other._handles.Length; j++)
  1206. {
  1207. (int otherLayer, int otherLevel) = other.GetLayerLevelForView(j);
  1208. MultiRange otherHandleRange = other.Storage.Range.Slice((ulong)other._allOffsets[j], 1);
  1209. ulong otherHandleBase = otherHandleRange.GetSubRange(0).Address;
  1210. if (handleBase == otherHandleBase)
  1211. {
  1212. // Check if the two sizes are compatible.
  1213. TextureInfo info = Storage.Info;
  1214. TextureInfo otherInfo = other.Storage.Info;
  1215. if (TextureCompatibility.ViewLayoutCompatible(info, otherInfo, level, otherLevel) &&
  1216. TextureCompatibility.CopySizeMatches(info, otherInfo, level, otherLevel))
  1217. {
  1218. // These textures are copy compatible. Create the dependency.
  1219. EnsureFullSubdivision();
  1220. other.EnsureFullSubdivision();
  1221. TextureGroupHandle handle = _handles[i];
  1222. TextureGroupHandle otherHandle = other._handles[j];
  1223. handle.CreateCopyDependency(otherHandle, copyTo);
  1224. // If "copyTo" is true, this texture must copy to the other.
  1225. // Otherwise, it must copy to this texture.
  1226. if (copyTo)
  1227. {
  1228. otherHandle.Copy(_context, handle);
  1229. }
  1230. else
  1231. {
  1232. handle.Copy(_context, otherHandle);
  1233. }
  1234. }
  1235. }
  1236. }
  1237. }
  1238. }
  1239. /// <summary>
  1240. /// Registers another texture group as an incompatible overlap, if not already registered.
  1241. /// </summary>
  1242. /// <param name="other">The texture group to add to the incompatible overlaps list</param>
  1243. /// <param name="copy">True if the overlap should register copy dependencies</param>
  1244. public void RegisterIncompatibleOverlap(TextureIncompatibleOverlap other, bool copy)
  1245. {
  1246. if (!_incompatibleOverlaps.Exists(overlap => overlap.Group == other.Group))
  1247. {
  1248. if (copy && other.Compatibility == TextureViewCompatibility.LayoutIncompatible)
  1249. {
  1250. // Any of the group's views may share compatibility, even if the parents do not fully.
  1251. CreateCopyDependency(other.Group, false);
  1252. }
  1253. _incompatibleOverlaps.Add(other);
  1254. other.Group._incompatibleOverlaps.Add(new TextureIncompatibleOverlap(this, other.Compatibility));
  1255. }
  1256. other.Group.SignalIncompatibleOverlapModified();
  1257. SignalIncompatibleOverlapModified();
  1258. }
  1259. /// <summary>
  1260. /// Clear modified flags in the given range.
  1261. /// This will stop any GPU written data from flushing or copying to dependent textures.
  1262. /// </summary>
  1263. /// <param name="range">The range to clear modified flags in</param>
  1264. /// <param name="ignore">Ignore handles that have a copy dependency to the specified group</param>
  1265. public void ClearModified(MultiRange range, TextureGroup ignore = null)
  1266. {
  1267. TextureGroupHandle[] handles = _handles;
  1268. foreach (TextureGroupHandle handle in handles)
  1269. {
  1270. // Handles list is not modified by another thread, only replaced, so this is thread safe.
  1271. // Remove modified flags from all overlapping handles, so that the textures don't flush to unmapped/remapped GPU memory.
  1272. MultiRange subRange = Storage.Range.Slice((ulong)handle.Offset, (ulong)handle.Size);
  1273. if (range.OverlapsWith(subRange))
  1274. {
  1275. if ((ignore == null || !handle.HasDependencyTo(ignore)) && handle.Modified)
  1276. {
  1277. handle.Modified = false;
  1278. Storage.SignalModifiedDirty();
  1279. lock (handle.Overlaps)
  1280. {
  1281. foreach (Texture texture in handle.Overlaps)
  1282. {
  1283. texture.SignalModifiedDirty();
  1284. }
  1285. }
  1286. }
  1287. }
  1288. }
  1289. Storage.SignalModifiedDirty();
  1290. if (_views != null)
  1291. {
  1292. foreach (Texture texture in _views)
  1293. {
  1294. texture.SignalModifiedDirty();
  1295. }
  1296. }
  1297. }
  1298. /// <summary>
  1299. /// A flush has been requested on a tracked region. Flush texture data for the given handle.
  1300. /// </summary>
  1301. /// <param name="handle">The handle this flush action is for</param>
  1302. /// <param name="address">The address of the flushing memory access</param>
  1303. /// <param name="size">The size of the flushing memory access</param>
  1304. public void FlushAction(TextureGroupHandle handle, ulong address, ulong size)
  1305. {
  1306. // If the page size is larger than 4KB, we will have a lot of false positives for flushing.
  1307. // Let's avoid flushing textures that are unlikely to be read from CPU to improve performance
  1308. // on those platforms.
  1309. if (!_physicalMemory.Supports4KBPages && !Storage.Info.IsLinear && !_context.IsGpuThread())
  1310. {
  1311. return;
  1312. }
  1313. // There is a small gap here where the action is removed but _actionRegistered is still 1.
  1314. // In this case it will skip registering the action, but here we are already handling it,
  1315. // so there shouldn't be any issue as it's the same handler for all actions.
  1316. handle.ClearActionRegistered();
  1317. if (!handle.Modified)
  1318. {
  1319. return;
  1320. }
  1321. bool isGpuThread = _context.IsGpuThread();
  1322. if (isGpuThread)
  1323. {
  1324. // No need to wait if we're on the GPU thread, we can just clear the modified flag immediately.
  1325. handle.Modified = false;
  1326. }
  1327. _context.Renderer.BackgroundContextAction(() =>
  1328. {
  1329. if (!isGpuThread)
  1330. {
  1331. handle.Sync(_context);
  1332. }
  1333. Storage.SignalModifiedDirty();
  1334. lock (handle.Overlaps)
  1335. {
  1336. foreach (Texture texture in handle.Overlaps)
  1337. {
  1338. texture.SignalModifiedDirty();
  1339. }
  1340. }
  1341. if (TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities))
  1342. {
  1343. FlushSliceRange(false, handle.BaseSlice, handle.BaseSlice + handle.SliceCount, Storage.GetFlushTexture());
  1344. }
  1345. });
  1346. }
  1347. /// <summary>
  1348. /// Dispose this texture group, disposing all related memory tracking handles.
  1349. /// </summary>
  1350. public void Dispose()
  1351. {
  1352. foreach (TextureGroupHandle group in _handles)
  1353. {
  1354. group.Dispose();
  1355. }
  1356. foreach (TextureIncompatibleOverlap incompatible in _incompatibleOverlaps)
  1357. {
  1358. incompatible.Group._incompatibleOverlaps.RemoveAll(overlap => overlap.Group == this);
  1359. }
  1360. }
  1361. }
  1362. }