TextureGroup.cs 56 KB

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