TextureGroup.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
  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.Range;
  6. using System;
  7. using System.Collections.Generic;
  8. namespace Ryujinx.Graphics.Gpu.Image
  9. {
  10. /// <summary>
  11. /// A texture group represents a group of textures that belong to the same storage.
  12. /// When views are created, this class will track memory accesses for them separately.
  13. /// The group iteratively adds more granular tracking as views of different kinds are added.
  14. /// Note that a texture group can be absorbed into another when it becomes a view parent.
  15. /// </summary>
  16. class TextureGroup : IDisposable
  17. {
  18. private delegate void HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false);
  19. /// <summary>
  20. /// The storage texture associated with this group.
  21. /// </summary>
  22. public Texture Storage { get; }
  23. /// <summary>
  24. /// Indicates if the texture has copy dependencies. If true, then all modifications
  25. /// must be signalled to the group, rather than skipping ones still to be flushed.
  26. /// </summary>
  27. public bool HasCopyDependencies { get; set; }
  28. private readonly GpuContext _context;
  29. private readonly PhysicalMemory _physicalMemory;
  30. private int[] _allOffsets;
  31. private int[] _sliceSizes;
  32. private bool _is3D;
  33. private bool _hasMipViews;
  34. private bool _hasLayerViews;
  35. private int _layers;
  36. private int _levels;
  37. private MultiRange TextureRange => Storage.Range;
  38. /// <summary>
  39. /// The views list from the storage texture.
  40. /// </summary>
  41. private List<Texture> _views;
  42. private TextureGroupHandle[] _handles;
  43. private bool[] _loadNeeded;
  44. /// <summary>
  45. /// Create a new texture group.
  46. /// </summary>
  47. /// <param name="context">GPU context that the texture group belongs to</param>
  48. /// <param name="physicalMemory">Physical memory where the <paramref name="storage"/> texture is mapped</param>
  49. /// <param name="storage">The storage texture for this group</param>
  50. public TextureGroup(GpuContext context, PhysicalMemory physicalMemory, Texture storage)
  51. {
  52. Storage = storage;
  53. _context = context;
  54. _physicalMemory = physicalMemory;
  55. _is3D = storage.Info.Target == Target.Texture3D;
  56. _layers = storage.Info.GetSlices();
  57. _levels = storage.Info.Levels;
  58. }
  59. /// <summary>
  60. /// Initialize a new texture group's dirty regions and offsets.
  61. /// </summary>
  62. /// <param name="size">Size info for the storage texture</param>
  63. /// <param name="hasLayerViews">True if the storage will have layer views</param>
  64. /// <param name="hasMipViews">True if the storage will have mip views</param>
  65. public void Initialize(ref SizeInfo size, bool hasLayerViews, bool hasMipViews)
  66. {
  67. _allOffsets = size.AllOffsets;
  68. _sliceSizes = size.SliceSizes;
  69. (_hasLayerViews, _hasMipViews) = PropagateGranularity(hasLayerViews, hasMipViews);
  70. RecalculateHandleRegions();
  71. }
  72. /// <summary>
  73. /// Check and optionally consume the dirty flags for a given texture.
  74. /// The state is shared between views of the same layers and levels.
  75. /// </summary>
  76. /// <param name="texture">The texture being used</param>
  77. /// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param>
  78. /// <returns>True if a flag was dirty, false otherwise</returns>
  79. public bool CheckDirty(Texture texture, bool consume)
  80. {
  81. bool dirty = false;
  82. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  83. {
  84. for (int i = 0; i < regionCount; i++)
  85. {
  86. TextureGroupHandle group = _handles[baseHandle + i];
  87. foreach (CpuRegionHandle handle in group.Handles)
  88. {
  89. if (handle.Dirty)
  90. {
  91. if (consume)
  92. {
  93. handle.Reprotect();
  94. }
  95. dirty = true;
  96. }
  97. }
  98. }
  99. });
  100. return dirty;
  101. }
  102. /// <summary>
  103. /// Synchronize memory for a given texture.
  104. /// If overlapping tracking handles are dirty, fully or partially synchronize the texture data.
  105. /// </summary>
  106. /// <param name="texture">The texture being used</param>
  107. public void SynchronizeMemory(Texture texture)
  108. {
  109. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  110. {
  111. bool dirty = false;
  112. bool anyModified = false;
  113. bool anyUnmapped = false;
  114. for (int i = 0; i < regionCount; i++)
  115. {
  116. TextureGroupHandle group = _handles[baseHandle + i];
  117. bool modified = group.Modified;
  118. bool handleDirty = false;
  119. bool handleModified = false;
  120. bool handleUnmapped = false;
  121. foreach (CpuRegionHandle handle in group.Handles)
  122. {
  123. if (handle.Dirty)
  124. {
  125. handle.Reprotect();
  126. handleDirty = true;
  127. }
  128. else
  129. {
  130. handleUnmapped |= handle.Unmapped;
  131. handleModified |= modified;
  132. }
  133. }
  134. // Evaluate if any copy dependencies need to be fulfilled. A few rules:
  135. // If the copy handle needs to be synchronized, prefer our own state.
  136. // If we need to be synchronized and there is a copy present, prefer the copy.
  137. if (group.NeedsCopy && group.Copy())
  138. {
  139. anyModified |= true; // The copy target has been modified.
  140. handleDirty = false;
  141. }
  142. else
  143. {
  144. anyModified |= handleModified;
  145. dirty |= handleDirty;
  146. }
  147. anyUnmapped |= handleUnmapped;
  148. if (group.NeedsCopy)
  149. {
  150. // The texture we copied from is still being written to. Copy from it again the next time this texture is used.
  151. texture.SignalGroupDirty();
  152. }
  153. _loadNeeded[baseHandle + i] = handleDirty && !handleUnmapped;
  154. }
  155. if (dirty)
  156. {
  157. if (anyUnmapped || (_handles.Length > 1 && (anyModified || split)))
  158. {
  159. // Partial texture invalidation. Only update the layers/levels with dirty flags of the storage.
  160. SynchronizePartial(baseHandle, regionCount);
  161. }
  162. else
  163. {
  164. // Full texture invalidation.
  165. texture.SynchronizeFull();
  166. }
  167. }
  168. });
  169. }
  170. /// <summary>
  171. /// Synchronize part of the storage texture, represented by a given range of handles.
  172. /// Only handles marked by the _loadNeeded array will be synchronized.
  173. /// </summary>
  174. /// <param name="baseHandle">The base index of the range of handles</param>
  175. /// <param name="regionCount">The number of handles to synchronize</param>
  176. private void SynchronizePartial(int baseHandle, int regionCount)
  177. {
  178. for (int i = 0; i < regionCount; i++)
  179. {
  180. if (_loadNeeded[baseHandle + i])
  181. {
  182. var info = GetHandleInformation(baseHandle + i);
  183. int offsetIndex = info.Index;
  184. // Only one of these will be greater than 1, as partial sync is only called when there are sub-image views.
  185. for (int layer = 0; layer < info.Layers; layer++)
  186. {
  187. for (int level = 0; level < info.Levels; level++)
  188. {
  189. int offset = _allOffsets[offsetIndex];
  190. int endOffset = (offsetIndex + 1 == _allOffsets.Length) ? (int)Storage.Size : _allOffsets[offsetIndex + 1];
  191. int size = endOffset - offset;
  192. ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
  193. data = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true);
  194. Storage.SetData(data, info.BaseLayer, info.BaseLevel);
  195. offsetIndex++;
  196. }
  197. }
  198. }
  199. }
  200. }
  201. /// <summary>
  202. /// Signal that a texture in the group has been modified by the GPU.
  203. /// </summary>
  204. /// <param name="texture">The texture that has been modified</param>
  205. /// <param name="registerAction">True if the flushing read action should be registered, false otherwise</param>
  206. public void SignalModified(Texture texture, bool registerAction)
  207. {
  208. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  209. {
  210. for (int i = 0; i < regionCount; i++)
  211. {
  212. TextureGroupHandle group = _handles[baseHandle + i];
  213. group.SignalModified();
  214. if (registerAction)
  215. {
  216. RegisterAction(group);
  217. }
  218. }
  219. });
  220. }
  221. /// <summary>
  222. /// Signal that a texture in the group is actively bound, or has been unbound by the GPU.
  223. /// </summary>
  224. /// <param name="texture">The texture that has been modified</param>
  225. /// <param name="bound">True if this texture is being bound, false if unbound</param>
  226. /// <param name="registerAction">True if the flushing read action should be registered, false otherwise</param>
  227. public void SignalModifying(Texture texture, bool bound, bool registerAction)
  228. {
  229. EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
  230. {
  231. for (int i = 0; i < regionCount; i++)
  232. {
  233. TextureGroupHandle group = _handles[baseHandle + i];
  234. group.SignalModifying(bound);
  235. if (registerAction)
  236. {
  237. RegisterAction(group);
  238. }
  239. }
  240. });
  241. }
  242. /// <summary>
  243. /// Register a read/write action to flush for a texture group.
  244. /// </summary>
  245. /// <param name="group">The group to register an action for</param>
  246. public void RegisterAction(TextureGroupHandle group)
  247. {
  248. foreach (CpuRegionHandle handle in group.Handles)
  249. {
  250. handle.RegisterAction((address, size) => FlushAction(group, address, size));
  251. }
  252. }
  253. /// <summary>
  254. /// Propagates the mip/layer view flags depending on the texture type.
  255. /// When the most granular type of subresource has views, the other type of subresource must be segmented granularly too.
  256. /// </summary>
  257. /// <param name="hasLayerViews">True if the storage has layer views</param>
  258. /// <param name="hasMipViews">True if the storage has mip views</param>
  259. /// <returns>The input values after propagation</returns>
  260. private (bool HasLayerViews, bool HasMipViews) PropagateGranularity(bool hasLayerViews, bool hasMipViews)
  261. {
  262. if (_is3D)
  263. {
  264. hasMipViews |= hasLayerViews;
  265. }
  266. else
  267. {
  268. hasLayerViews |= hasMipViews;
  269. }
  270. return (hasLayerViews, hasMipViews);
  271. }
  272. /// <summary>
  273. /// Evaluate the range of tracking handles which a view texture overlaps with.
  274. /// </summary>
  275. /// <param name="texture">The texture to get handles for</param>
  276. /// <param name="callback">
  277. /// 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.
  278. /// This can be called for multiple disjoint ranges, if required.
  279. /// </param>
  280. private void EvaluateRelevantHandles(Texture texture, HandlesCallbackDelegate callback)
  281. {
  282. if (texture == Storage || !(_hasMipViews || _hasLayerViews))
  283. {
  284. callback(0, _handles.Length);
  285. return;
  286. }
  287. EvaluateRelevantHandles(texture.FirstLayer, texture.FirstLevel, texture.Info.GetSlices(), texture.Info.Levels, callback);
  288. }
  289. /// <summary>
  290. /// Evaluate the range of tracking handles which a view texture overlaps with,
  291. /// using the view's position and slice/level counts.
  292. /// </summary>
  293. /// <param name="firstLayer">The first layer of the texture</param>
  294. /// <param name="firstLevel">The first level of the texture</param>
  295. /// <param name="slices">The slice count of the texture</param>
  296. /// <param name="levels">The level count of the texture</param>
  297. /// <param name="callback">
  298. /// 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.
  299. /// This can be called for multiple disjoint ranges, if required.
  300. /// </param>
  301. private void EvaluateRelevantHandles(int firstLayer, int firstLevel, int slices, int levels, HandlesCallbackDelegate callback)
  302. {
  303. int targetLayerHandles = _hasLayerViews ? slices : 1;
  304. int targetLevelHandles = _hasMipViews ? levels : 1;
  305. if (_is3D)
  306. {
  307. // Future mip levels come after all layers of the last mip level. Each mipmap has less layers (depth) than the last.
  308. if (!_hasLayerViews)
  309. {
  310. // When there are no layer views, the mips are at a consistent offset.
  311. callback(firstLevel, targetLevelHandles);
  312. }
  313. else
  314. {
  315. (int levelIndex, int layerCount) = Get3DLevelRange(firstLevel);
  316. if (levels > 1 && slices < _layers)
  317. {
  318. // The given texture only covers some of the depth of multiple mips. (a "depth slice")
  319. // Callback with each mip's range separately.
  320. // Can assume that the group is fully subdivided (both slices and levels > 1 for storage)
  321. while (levels-- > 1)
  322. {
  323. callback(firstLayer + levelIndex, slices);
  324. levelIndex += layerCount;
  325. layerCount = Math.Max(layerCount >> 1, 1);
  326. slices = Math.Max(layerCount >> 1, 1);
  327. }
  328. }
  329. else
  330. {
  331. int totalSize = Math.Min(layerCount, slices);
  332. while (levels-- > 1)
  333. {
  334. layerCount = Math.Max(layerCount >> 1, 1);
  335. totalSize += layerCount;
  336. }
  337. callback(firstLayer + levelIndex, totalSize);
  338. }
  339. }
  340. }
  341. else
  342. {
  343. // Future layers come after all mipmaps of the last.
  344. int levelHandles = _hasMipViews ? _levels : 1;
  345. if (slices > 1 && levels < _levels)
  346. {
  347. // The given texture only covers some of the mipmaps of multiple slices. (a "mip slice")
  348. // Callback with each layer's range separately.
  349. // Can assume that the group is fully subdivided (both slices and levels > 1 for storage)
  350. for (int i = 0; i < slices; i++)
  351. {
  352. callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true);
  353. }
  354. }
  355. else
  356. {
  357. callback(firstLevel + firstLayer * levelHandles, targetLevelHandles + (targetLayerHandles - 1) * levelHandles);
  358. }
  359. }
  360. }
  361. /// <summary>
  362. /// Get the range of offsets for a given mip level of a 3D texture.
  363. /// </summary>
  364. /// <param name="level">The level to return</param>
  365. /// <returns>Start index and count of offsets for the given level</returns>
  366. private (int Index, int Count) Get3DLevelRange(int level)
  367. {
  368. int index = 0;
  369. int count = _layers; // Depth. Halves with each mip level.
  370. while (level-- > 0)
  371. {
  372. index += count;
  373. count = Math.Max(count >> 1, 1);
  374. }
  375. return (index, count);
  376. }
  377. /// <summary>
  378. /// Get view information for a single tracking handle.
  379. /// </summary>
  380. /// <param name="handleIndex">The index of the handle</param>
  381. /// <returns>The layers and levels that the handle covers, and its index in the offsets array</returns>
  382. private (int BaseLayer, int BaseLevel, int Levels, int Layers, int Index) GetHandleInformation(int handleIndex)
  383. {
  384. int baseLayer;
  385. int baseLevel;
  386. int levels = _hasMipViews ? 1 : _levels;
  387. int layers = _hasLayerViews ? 1 : _layers;
  388. int index;
  389. if (_is3D)
  390. {
  391. if (_hasLayerViews)
  392. {
  393. // NOTE: Will also have mip views, or only one level in storage.
  394. index = handleIndex;
  395. baseLevel = 0;
  396. int levelLayers = _layers;
  397. while (handleIndex >= levelLayers)
  398. {
  399. handleIndex -= levelLayers;
  400. baseLevel++;
  401. levelLayers = Math.Max(levelLayers >> 1, 1);
  402. }
  403. baseLayer = handleIndex;
  404. }
  405. else
  406. {
  407. baseLayer = 0;
  408. baseLevel = handleIndex;
  409. (index, _) = Get3DLevelRange(baseLevel);
  410. }
  411. }
  412. else
  413. {
  414. baseLevel = _hasMipViews ? handleIndex % _levels : 0;
  415. baseLayer = _hasMipViews ? handleIndex / _levels : handleIndex;
  416. index = baseLevel + baseLayer * _levels;
  417. }
  418. return (baseLayer, baseLevel, levels, layers, index);
  419. }
  420. /// <summary>
  421. /// Gets the layer and level for a given view.
  422. /// </summary>
  423. /// <param name="index">The index of the view</param>
  424. /// <returns>The layer and level of the specified view</returns>
  425. private (int BaseLayer, int BaseLevel) GetLayerLevelForView(int index)
  426. {
  427. if (_is3D)
  428. {
  429. int baseLevel = 0;
  430. int levelLayers = _layers;
  431. while (index >= levelLayers)
  432. {
  433. index -= levelLayers;
  434. baseLevel++;
  435. levelLayers = Math.Max(levelLayers >> 1, 1);
  436. }
  437. return (index, baseLevel);
  438. }
  439. else
  440. {
  441. return (index / _levels, index % _levels);
  442. }
  443. }
  444. /// <summary>
  445. /// Find the byte offset of a given texture relative to the storage.
  446. /// </summary>
  447. /// <param name="texture">The texture to locate</param>
  448. /// <returns>The offset of the texture in bytes</returns>
  449. public int FindOffset(Texture texture)
  450. {
  451. return _allOffsets[GetOffsetIndex(texture.FirstLayer, texture.FirstLevel)];
  452. }
  453. /// <summary>
  454. /// Find the offset index of a given layer and level.
  455. /// </summary>
  456. /// <param name="layer">The view layer</param>
  457. /// <param name="level">The view level</param>
  458. /// <returns>The offset index of the given layer and level</returns>
  459. public int GetOffsetIndex(int layer, int level)
  460. {
  461. if (_is3D)
  462. {
  463. return layer + Get3DLevelRange(level).Index;
  464. }
  465. else
  466. {
  467. return level + layer * _levels;
  468. }
  469. }
  470. /// <summary>
  471. /// The action to perform when a memory tracking handle is flipped to dirty.
  472. /// This notifies overlapping textures that the memory needs to be synchronized.
  473. /// </summary>
  474. /// <param name="groupHandle">The handle that a dirty flag was set on</param>
  475. private void DirtyAction(TextureGroupHandle groupHandle)
  476. {
  477. // Notify all textures that belong to this handle.
  478. Storage.SignalGroupDirty();
  479. lock (groupHandle.Overlaps)
  480. {
  481. foreach (Texture overlap in groupHandle.Overlaps)
  482. {
  483. overlap.SignalGroupDirty();
  484. }
  485. }
  486. }
  487. /// <summary>
  488. /// Generate a CpuRegionHandle for a given address and size range in CPU VA.
  489. /// </summary>
  490. /// <param name="address">The start address of the tracked region</param>
  491. /// <param name="size">The size of the tracked region</param>
  492. /// <returns>A CpuRegionHandle covering the given range</returns>
  493. private CpuRegionHandle GenerateHandle(ulong address, ulong size)
  494. {
  495. return _physicalMemory.BeginTracking(address, size);
  496. }
  497. /// <summary>
  498. /// Generate a TextureGroupHandle covering a specified range of views.
  499. /// </summary>
  500. /// <param name="viewStart">The start view of the handle</param>
  501. /// <param name="views">The number of views to cover</param>
  502. /// <returns>A TextureGroupHandle covering the given views</returns>
  503. private TextureGroupHandle GenerateHandles(int viewStart, int views)
  504. {
  505. int offset = _allOffsets[viewStart];
  506. int endOffset = (viewStart + views == _allOffsets.Length) ? (int)Storage.Size : _allOffsets[viewStart + views];
  507. int size = endOffset - offset;
  508. var result = new List<CpuRegionHandle>();
  509. for (int i = 0; i < TextureRange.Count; i++)
  510. {
  511. MemoryRange item = TextureRange.GetSubRange(i);
  512. int subRangeSize = (int)item.Size;
  513. int sliceStart = Math.Clamp(offset, 0, subRangeSize);
  514. int sliceEnd = Math.Clamp(endOffset, 0, subRangeSize);
  515. if (sliceStart != sliceEnd)
  516. {
  517. result.Add(GenerateHandle(item.Address + (ulong)sliceStart, (ulong)(sliceEnd - sliceStart)));
  518. }
  519. offset -= subRangeSize;
  520. endOffset -= subRangeSize;
  521. if (endOffset <= 0)
  522. {
  523. break;
  524. }
  525. }
  526. (int firstLayer, int firstLevel) = GetLayerLevelForView(viewStart);
  527. if (_hasLayerViews && _hasMipViews)
  528. {
  529. size = _sliceSizes[firstLevel];
  530. }
  531. var groupHandle = new TextureGroupHandle(this, _allOffsets[viewStart], (ulong)size, _views, firstLayer, firstLevel, result.ToArray());
  532. foreach (CpuRegionHandle handle in result)
  533. {
  534. handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
  535. }
  536. return groupHandle;
  537. }
  538. /// <summary>
  539. /// Update the views in this texture group, rebuilding the memory tracking if required.
  540. /// </summary>
  541. /// <param name="views">The views list of the storage texture</param>
  542. public void UpdateViews(List<Texture> views)
  543. {
  544. // This is saved to calculate overlapping views for each handle.
  545. _views = views;
  546. bool layerViews = _hasLayerViews;
  547. bool mipViews = _hasMipViews;
  548. bool regionsRebuilt = false;
  549. if (!(layerViews && mipViews))
  550. {
  551. foreach (Texture view in views)
  552. {
  553. if (view.Info.GetSlices() < _layers)
  554. {
  555. layerViews = true;
  556. }
  557. if (view.Info.Levels < _levels)
  558. {
  559. mipViews = true;
  560. }
  561. }
  562. (layerViews, mipViews) = PropagateGranularity(layerViews, mipViews);
  563. if (layerViews != _hasLayerViews || mipViews != _hasMipViews)
  564. {
  565. _hasLayerViews = layerViews;
  566. _hasMipViews = mipViews;
  567. RecalculateHandleRegions();
  568. regionsRebuilt = true;
  569. }
  570. }
  571. if (!regionsRebuilt)
  572. {
  573. // Must update the overlapping views on all handles, but only if they were not just recreated.
  574. foreach (TextureGroupHandle handle in _handles)
  575. {
  576. handle.RecalculateOverlaps(this, views);
  577. }
  578. }
  579. Storage.SignalGroupDirty();
  580. foreach (Texture texture in views)
  581. {
  582. texture.SignalGroupDirty();
  583. }
  584. }
  585. /// <summary>
  586. /// Inherit handle state from an old set of handles, such as modified and dirty flags.
  587. /// </summary>
  588. /// <param name="oldHandles">The set of handles to inherit state from</param>
  589. /// <param name="handles">The set of handles inheriting the state</param>
  590. private void InheritHandles(TextureGroupHandle[] oldHandles, TextureGroupHandle[] handles)
  591. {
  592. foreach (var group in handles)
  593. {
  594. foreach (var handle in group.Handles)
  595. {
  596. bool dirty = false;
  597. foreach (var oldGroup in oldHandles)
  598. {
  599. if (group.OverlapsWith(oldGroup.Offset, oldGroup.Size))
  600. {
  601. foreach (var oldHandle in oldGroup.Handles)
  602. {
  603. if (handle.OverlapsWith(oldHandle.Address, oldHandle.Size))
  604. {
  605. dirty |= oldHandle.Dirty;
  606. }
  607. }
  608. group.Inherit(oldGroup);
  609. }
  610. }
  611. if (dirty && !handle.Dirty)
  612. {
  613. handle.Reprotect(true);
  614. }
  615. if (group.Modified)
  616. {
  617. handle.RegisterAction((address, size) => FlushAction(group, address, size));
  618. }
  619. }
  620. }
  621. }
  622. /// <summary>
  623. /// Inherit state from another texture group.
  624. /// </summary>
  625. /// <param name="other">The texture group to inherit from</param>
  626. public void Inherit(TextureGroup other)
  627. {
  628. bool layerViews = _hasLayerViews || other._hasLayerViews;
  629. bool mipViews = _hasMipViews || other._hasMipViews;
  630. if (layerViews != _hasLayerViews || mipViews != _hasMipViews)
  631. {
  632. _hasLayerViews = layerViews;
  633. _hasMipViews = mipViews;
  634. RecalculateHandleRegions();
  635. }
  636. InheritHandles(other._handles, _handles);
  637. }
  638. /// <summary>
  639. /// Replace the current handles with the new handles. It is assumed that the new handles start dirty.
  640. /// The dirty flags from the previous handles will be kept.
  641. /// </summary>
  642. /// <param name="handles">The handles to replace the current handles with</param>
  643. private void ReplaceHandles(TextureGroupHandle[] handles)
  644. {
  645. if (_handles != null)
  646. {
  647. // When replacing handles, they should start as non-dirty.
  648. foreach (TextureGroupHandle groupHandle in handles)
  649. {
  650. foreach (CpuRegionHandle handle in groupHandle.Handles)
  651. {
  652. handle.Reprotect();
  653. }
  654. }
  655. InheritHandles(_handles, handles);
  656. foreach (var oldGroup in _handles)
  657. {
  658. foreach (var oldHandle in oldGroup.Handles)
  659. {
  660. oldHandle.Dispose();
  661. }
  662. }
  663. }
  664. _handles = handles;
  665. _loadNeeded = new bool[_handles.Length];
  666. }
  667. /// <summary>
  668. /// Recalculate handle regions for this texture group, and inherit existing state into the new handles.
  669. /// </summary>
  670. private void RecalculateHandleRegions()
  671. {
  672. TextureGroupHandle[] handles;
  673. if (!(_hasMipViews || _hasLayerViews))
  674. {
  675. // Single dirty region.
  676. var cpuRegionHandles = new CpuRegionHandle[TextureRange.Count];
  677. for (int i = 0; i < TextureRange.Count; i++)
  678. {
  679. var currentRange = TextureRange.GetSubRange(i);
  680. cpuRegionHandles[i] = GenerateHandle(currentRange.Address, currentRange.Size);
  681. }
  682. var groupHandle = new TextureGroupHandle(this, 0, Storage.Size, _views, 0, 0, cpuRegionHandles);
  683. foreach (CpuRegionHandle handle in cpuRegionHandles)
  684. {
  685. handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
  686. }
  687. handles = new TextureGroupHandle[] { groupHandle };
  688. }
  689. else
  690. {
  691. // Get views for the host texture.
  692. // 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.
  693. // Depending on if the texture is 3d, either the mip views imply that layer views are present (2d) or the other way around (3d).
  694. // This is enforced by the way the texture matched as a view, so we don't need to check.
  695. int layerHandles = _hasLayerViews ? _layers : 1;
  696. int levelHandles = _hasMipViews ? _levels : 1;
  697. int handleIndex = 0;
  698. if (_is3D)
  699. {
  700. var handlesList = new List<TextureGroupHandle>();
  701. for (int i = 0; i < levelHandles; i++)
  702. {
  703. for (int j = 0; j < layerHandles; j++)
  704. {
  705. (int viewStart, int views) = Get3DLevelRange(i);
  706. viewStart += j;
  707. views = _hasLayerViews ? 1 : views; // A layer view is also a mip view.
  708. handlesList.Add(GenerateHandles(viewStart, views));
  709. }
  710. layerHandles = Math.Max(1, layerHandles >> 1);
  711. }
  712. handles = handlesList.ToArray();
  713. }
  714. else
  715. {
  716. handles = new TextureGroupHandle[layerHandles * levelHandles];
  717. for (int i = 0; i < layerHandles; i++)
  718. {
  719. for (int j = 0; j < levelHandles; j++)
  720. {
  721. int viewStart = j + i * _levels;
  722. int views = _hasMipViews ? 1 : _levels; // A mip view is also a layer view.
  723. handles[handleIndex++] = GenerateHandles(viewStart, views);
  724. }
  725. }
  726. }
  727. }
  728. ReplaceHandles(handles);
  729. }
  730. /// <summary>
  731. /// Ensure that there is a handle for each potential texture view. Required for copy dependencies to work.
  732. /// </summary>
  733. private void EnsureFullSubdivision()
  734. {
  735. if (!(_hasLayerViews && _hasMipViews))
  736. {
  737. _hasLayerViews = true;
  738. _hasMipViews = true;
  739. RecalculateHandleRegions();
  740. }
  741. }
  742. /// <summary>
  743. /// Create a copy dependency between this texture group, and a texture at a given layer/level offset.
  744. /// </summary>
  745. /// <param name="other">The view compatible texture to create a dependency to</param>
  746. /// <param name="firstLayer">The base layer of the given texture relative to the storage</param>
  747. /// <param name="firstLevel">The base level of the given texture relative to the storage</param>
  748. /// <param name="copyTo">True if this texture is first copied to the given one, false for the opposite direction</param>
  749. public void CreateCopyDependency(Texture other, int firstLayer, int firstLevel, bool copyTo)
  750. {
  751. TextureGroup otherGroup = other.Group;
  752. EnsureFullSubdivision();
  753. otherGroup.EnsureFullSubdivision();
  754. // Get the location of each texture within its storage, so we can find the handles to apply the dependency to.
  755. // This can consist of multiple disjoint regions, for example if this is a mip slice of an array texture.
  756. var targetRange = new List<(int BaseHandle, int RegionCount)>();
  757. var otherRange = new List<(int BaseHandle, int RegionCount)>();
  758. EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, split) => targetRange.Add((baseHandle, regionCount)));
  759. otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, split) => otherRange.Add((baseHandle, regionCount)));
  760. int targetIndex = 0;
  761. int otherIndex = 0;
  762. (int Handle, int RegionCount) targetRegion = (0, 0);
  763. (int Handle, int RegionCount) otherRegion = (0, 0);
  764. while (true)
  765. {
  766. if (targetRegion.RegionCount == 0)
  767. {
  768. if (targetIndex >= targetRange.Count)
  769. {
  770. break;
  771. }
  772. targetRegion = targetRange[targetIndex++];
  773. }
  774. if (otherRegion.RegionCount == 0)
  775. {
  776. if (otherIndex >= otherRange.Count)
  777. {
  778. break;
  779. }
  780. otherRegion = otherRange[otherIndex++];
  781. }
  782. TextureGroupHandle handle = _handles[targetRegion.Handle++];
  783. TextureGroupHandle otherHandle = other.Group._handles[otherRegion.Handle++];
  784. targetRegion.RegionCount--;
  785. otherRegion.RegionCount--;
  786. handle.CreateCopyDependency(otherHandle, copyTo);
  787. // If "copyTo" is true, this texture must copy to the other.
  788. // Otherwise, it must copy to this texture.
  789. if (copyTo)
  790. {
  791. otherHandle.Copy(handle);
  792. }
  793. else
  794. {
  795. handle.Copy(otherHandle);
  796. }
  797. }
  798. }
  799. /// <summary>
  800. /// A flush has been requested on a tracked region. Find an appropriate view to flush.
  801. /// </summary>
  802. /// <param name="handle">The handle this flush action is for</param>
  803. /// <param name="address">The address of the flushing memory access</param>
  804. /// <param name="size">The size of the flushing memory access</param>
  805. public void FlushAction(TextureGroupHandle handle, ulong address, ulong size)
  806. {
  807. Storage.ExternalFlush(address, size);
  808. lock (handle.Overlaps)
  809. {
  810. foreach (Texture overlap in handle.Overlaps)
  811. {
  812. overlap.ExternalFlush(address, size);
  813. }
  814. }
  815. handle.Modified = false;
  816. }
  817. /// <summary>
  818. /// Dispose this texture group, disposing all related memory tracking handles.
  819. /// </summary>
  820. public void Dispose()
  821. {
  822. foreach (TextureGroupHandle group in _handles)
  823. {
  824. group.Dispose();
  825. }
  826. }
  827. }
  828. }