TextureGroup.cs 55 KB

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